aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
Diffstat (limited to 'executor')
-rw-r--r--executor/common_linux.h188
1 files changed, 188 insertions, 0 deletions
diff --git a/executor/common_linux.h b/executor/common_linux.h
index b49e67542..329d865d0 100644
--- a/executor/common_linux.h
+++ b/executor/common_linux.h
@@ -1563,6 +1563,194 @@ static long syz_io_uring_submit(volatile long a0, volatile long a1, volatile lon
#endif
+#if SYZ_EXECUTOR || __NR_syz_btf_id_by_name
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+// Some items in linux/btf.h are relatively new, so we copy them here for
+// backward compatibility.
+#define BTF_MAGIC 0xeB9F
+
+struct btf_header {
+ __u16 magic;
+ __u8 version;
+ __u8 flags;
+ __u32 hdr_len;
+ __u32 type_off;
+ __u32 type_len;
+ __u32 str_off;
+ __u32 str_len;
+};
+
+#define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f)
+#define BTF_INFO_VLEN(info) ((info)&0xffff)
+
+#define BTF_KIND_INT 1
+#define BTF_KIND_ARRAY 3
+#define BTF_KIND_STRUCT 4
+#define BTF_KIND_UNION 5
+#define BTF_KIND_ENUM 6
+#define BTF_KIND_FUNC_PROTO 13
+#define BTF_KIND_VAR 14
+#define BTF_KIND_DATASEC 15
+
+struct btf_type {
+ __u32 name_off;
+ __u32 info;
+ union {
+ __u32 size;
+ __u32 type;
+ };
+};
+
+struct btf_enum {
+ __u32 name_off;
+ __s32 val;
+};
+
+struct btf_array {
+ __u32 type;
+ __u32 index_type;
+ __u32 nelems;
+};
+
+struct btf_member {
+ __u32 name_off;
+ __u32 type;
+ __u32 offset;
+};
+
+struct btf_param {
+ __u32 name_off;
+ __u32 type;
+};
+
+struct btf_var {
+ __u32 linkage;
+};
+
+struct btf_var_secinfo {
+ __u32 type;
+ __u32 offset;
+ __u32 size;
+};
+
+// Set the limit on the maximum size of btf/vmlinux to be 10 MiB.
+#define VMLINUX_MAX_SUPPORT_SIZE (10 * 1024 * 1024)
+
+// Read out all the content of /sys/kernel/btf/vmlinux to the fixed address
+// buffer and return it. Return NULL if failed.
+static char* read_btf_vmlinux()
+{
+ static bool is_read = false;
+ static char buf[VMLINUX_MAX_SUPPORT_SIZE];
+
+ // There could be a race condition here, but it should not be harmful.
+ if (is_read)
+ return buf;
+
+ int fd = open("/sys/kernel/btf/vmlinux", O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ unsigned long bytes_read = 0;
+ for (;;) {
+ ssize_t ret = read(fd, buf + bytes_read,
+ VMLINUX_MAX_SUPPORT_SIZE - bytes_read);
+
+ if (ret < 0 || bytes_read + ret == VMLINUX_MAX_SUPPORT_SIZE)
+ return NULL;
+
+ if (ret == 0)
+ break;
+
+ bytes_read += ret;
+ }
+
+ is_read = true;
+ return buf;
+}
+
+// Given a pointer to a C-string as the only argument a0, return the
+// corresponding btf ID for this name. Return -1 if there is an error when
+// opening the vmlinux file or the name is not found in vmlinux.
+static long syz_btf_id_by_name(volatile long a0)
+{
+ // syzlang: syz_btf_id_by_name(name ptr[in, string]) btf_id
+ // C: syz_btf_id_by_name(char* name)
+ char* target = (char*)a0;
+
+ char* vmlinux = read_btf_vmlinux();
+ if (vmlinux == NULL)
+ return -1;
+
+ struct btf_header* btf_header = (struct btf_header*)vmlinux;
+ if (btf_header->magic != BTF_MAGIC)
+ return -1;
+ // These offsets are bytes relative to the end of the header.
+ char* btf_type_sec = vmlinux + btf_header->hdr_len + btf_header->type_off;
+ char* btf_str_sec = vmlinux + btf_header->hdr_len + btf_header->str_off;
+ // Scan through the btf type section, and find a type description that
+ // matches the provided name.
+ unsigned int bytes_parsed = 0;
+ // BTF index starts at 1.
+ long idx = 1;
+ while (bytes_parsed < btf_header->type_len) {
+ struct btf_type* btf_type = (struct btf_type*)(btf_type_sec + bytes_parsed);
+ uint32 kind = BTF_INFO_KIND(btf_type->info);
+ uint32 vlen = BTF_INFO_VLEN(btf_type->info);
+ char* name = btf_str_sec + btf_type->name_off;
+
+ if (strcmp(name, target) == 0)
+ return idx;
+
+ // From /include/uapi/linux/btf.h, some kinds of types are
+ // followed by extra data.
+ size_t skip;
+ switch (kind) {
+ case BTF_KIND_INT:
+ skip = sizeof(uint32);
+ break;
+ case BTF_KIND_ENUM:
+ skip = sizeof(struct btf_enum) * vlen;
+ break;
+ case BTF_KIND_ARRAY:
+ skip = sizeof(struct btf_array);
+ break;
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ skip = sizeof(struct btf_member) * vlen;
+ break;
+ case BTF_KIND_FUNC_PROTO:
+ skip = sizeof(struct btf_param) * vlen;
+ break;
+ case BTF_KIND_VAR:
+ skip = sizeof(struct btf_var);
+ break;
+ case BTF_KIND_DATASEC:
+ skip = sizeof(struct btf_var_secinfo) * vlen;
+ break;
+ default:
+ skip = 0;
+ }
+
+ bytes_parsed += sizeof(struct btf_type) + skip;
+ idx++;
+ }
+
+ return -1;
+}
+
+#endif // SYZ_EXECUTOR || __NR_syz_btf_id_by_name
+
// Same as memcpy except that it accepts offset to dest and src.
#if SYZ_EXECUTOR || __NR_syz_memcpy_off
static long syz_memcpy_off(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4)