* [PATCH v5 1/6] fs/procfs: extract logic for getting VMA name constituents
2024-06-18 22:45 [PATCH v5 0/6] ioctl()-based API to query VMAs from /proc/<pid>/maps Andrii Nakryiko
@ 2024-06-18 22:45 ` Andrii Nakryiko
2024-06-18 22:45 ` [PATCH v5 2/6] fs/procfs: implement efficient VMA querying API for /proc/<pid>/maps Andrii Nakryiko
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Andrii Nakryiko @ 2024-06-18 22:45 UTC (permalink / raw)
To: linux-fsdevel, brauner, viro, akpm
Cc: linux-kernel, bpf, gregkh, linux-mm, liam.howlett, surenb, rppt,
adobriyan, Andrii Nakryiko
Extract generic logic to fetch relevant pieces of data to describe VMA
name. This could be just some string (either special constant or
user-provided), or a string with some formatted wrapping text (e.g.,
"[anon_shmem:<something>]"), or, commonly, file path. seq_file-based
logic has different methods to handle all three cases, but they are
currently mixed in with extracting underlying sources of data.
This patch splits this into data fetching and data formatting, so that
data fetching can be reused later on.
There should be no functional changes.
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
fs/proc/task_mmu.c | 125 +++++++++++++++++++++++++--------------------
1 file changed, 71 insertions(+), 54 deletions(-)
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 93fb2c61b154..507b7dc7c4c8 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -239,6 +239,67 @@ static int do_maps_open(struct inode *inode, struct file *file,
sizeof(struct proc_maps_private));
}
+static void get_vma_name(struct vm_area_struct *vma,
+ const struct path **path,
+ const char **name,
+ const char **name_fmt)
+{
+ struct anon_vma_name *anon_name = vma->vm_mm ? anon_vma_name(vma) : NULL;
+
+ *name = NULL;
+ *path = NULL;
+ *name_fmt = NULL;
+
+ /*
+ * Print the dentry name for named mappings, and a
+ * special [heap] marker for the heap:
+ */
+ if (vma->vm_file) {
+ /*
+ * If user named this anon shared memory via
+ * prctl(PR_SET_VMA ..., use the provided name.
+ */
+ if (anon_name) {
+ *name_fmt = "[anon_shmem:%s]";
+ *name = anon_name->name;
+ } else {
+ *path = file_user_path(vma->vm_file);
+ }
+ return;
+ }
+
+ if (vma->vm_ops && vma->vm_ops->name) {
+ *name = vma->vm_ops->name(vma);
+ if (*name)
+ return;
+ }
+
+ *name = arch_vma_name(vma);
+ if (*name)
+ return;
+
+ if (!vma->vm_mm) {
+ *name = "[vdso]";
+ return;
+ }
+
+ if (vma_is_initial_heap(vma)) {
+ *name = "[heap]";
+ return;
+ }
+
+ if (vma_is_initial_stack(vma)) {
+ *name = "[stack]";
+ return;
+ }
+
+ if (anon_name) {
+ *name_fmt = "[anon:%s]";
+ *name = anon_name->name;
+ return;
+ }
+}
+
static void show_vma_header_prefix(struct seq_file *m,
unsigned long start, unsigned long end,
vm_flags_t flags, unsigned long long pgoff,
@@ -262,17 +323,15 @@ static void show_vma_header_prefix(struct seq_file *m,
static void
show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
{
- struct anon_vma_name *anon_name = NULL;
- struct mm_struct *mm = vma->vm_mm;
- struct file *file = vma->vm_file;
+ const struct path *path;
+ const char *name_fmt, *name;
vm_flags_t flags = vma->vm_flags;
unsigned long ino = 0;
unsigned long long pgoff = 0;
unsigned long start, end;
dev_t dev = 0;
- const char *name = NULL;
- if (file) {
+ if (vma->vm_file) {
const struct inode *inode = file_user_inode(vma->vm_file);
dev = inode->i_sb->s_dev;
@@ -283,57 +342,15 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
start = vma->vm_start;
end = vma->vm_end;
show_vma_header_prefix(m, start, end, flags, pgoff, dev, ino);
- if (mm)
- anon_name = anon_vma_name(vma);
- /*
- * Print the dentry name for named mappings, and a
- * special [heap] marker for the heap:
- */
- if (file) {
+ get_vma_name(vma, &path, &name, &name_fmt);
+ if (path) {
seq_pad(m, ' ');
- /*
- * If user named this anon shared memory via
- * prctl(PR_SET_VMA ..., use the provided name.
- */
- if (anon_name)
- seq_printf(m, "[anon_shmem:%s]", anon_name->name);
- else
- seq_path(m, file_user_path(file), "\n");
- goto done;
- }
-
- if (vma->vm_ops && vma->vm_ops->name) {
- name = vma->vm_ops->name(vma);
- if (name)
- goto done;
- }
-
- name = arch_vma_name(vma);
- if (!name) {
- if (!mm) {
- name = "[vdso]";
- goto done;
- }
-
- if (vma_is_initial_heap(vma)) {
- name = "[heap]";
- goto done;
- }
-
- if (vma_is_initial_stack(vma)) {
- name = "[stack]";
- goto done;
- }
-
- if (anon_name) {
- seq_pad(m, ' ');
- seq_printf(m, "[anon:%s]", anon_name->name);
- }
- }
-
-done:
- if (name) {
+ seq_path(m, path, "\n");
+ } else if (name_fmt) {
+ seq_pad(m, ' ');
+ seq_printf(m, name_fmt, name);
+ } else if (name) {
seq_pad(m, ' ');
seq_puts(m, name);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 2/6] fs/procfs: implement efficient VMA querying API for /proc/<pid>/maps
2024-06-18 22:45 [PATCH v5 0/6] ioctl()-based API to query VMAs from /proc/<pid>/maps Andrii Nakryiko
2024-06-18 22:45 ` [PATCH v5 1/6] fs/procfs: extract logic for getting VMA name constituents Andrii Nakryiko
@ 2024-06-18 22:45 ` Andrii Nakryiko
2024-06-26 2:42 ` Liam R. Howlett
2024-06-18 22:45 ` [PATCH v5 3/6] fs/procfs: add build ID fetching to PROCMAP_QUERY API Andrii Nakryiko
` (3 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Andrii Nakryiko @ 2024-06-18 22:45 UTC (permalink / raw)
To: linux-fsdevel, brauner, viro, akpm
Cc: linux-kernel, bpf, gregkh, linux-mm, liam.howlett, surenb, rppt,
adobriyan, Andrii Nakryiko
/proc/<pid>/maps file is extremely useful in practice for various tasks
involving figuring out process memory layout, what files are backing any
given memory range, etc. One important class of applications that
absolutely rely on this are profilers/stack symbolizers (perf tool being one
of them). Patterns of use differ, but they generally would fall into two
categories.
In on-demand pattern, a profiler/symbolizer would normally capture stack
trace containing absolute memory addresses of some functions, and would
then use /proc/<pid>/maps file to find corresponding backing ELF files
(normally, only executable VMAs are of interest), file offsets within
them, and then continue from there to get yet more information (ELF
symbols, DWARF information) to get human-readable symbolic information.
This pattern is used by Meta's fleet-wide profiler, as one example.
In preprocessing pattern, application doesn't know the set of addresses
of interest, so it has to fetch all relevant VMAs (again, probably only
executable ones), store or cache them, then proceed with profiling and
stack trace capture. Once done, it would do symbolization based on
stored VMA information. This can happen at much later point in time.
This patterns is used by perf tool, as an example.
In either case, there are both performance and correctness requirement
involved. This address to VMA information translation has to be done as
efficiently as possible, but also not miss any VMA (especially in the
case of loading/unloading shared libraries). In practice, correctness
can't be guaranteed (due to process dying before VMA data can be
captured, or shared library being unloaded, etc), but any effort to
maximize the chance of finding the VMA is appreciated.
Unfortunately, for all the /proc/<pid>/maps file universality and
usefulness, it doesn't fit the above use cases 100%.
First, it's main purpose is to emit all VMAs sequentially, but in
practice captured addresses would fall only into a smaller subset of all
process' VMAs, mainly containing executable text. Yet, library would
need to parse most or all of the contents to find needed VMAs, as there
is no way to skip VMAs that are of no use. Efficient library can do the
linear pass and it is still relatively efficient, but it's definitely an
overhead that can be avoided, if there was a way to do more targeted
querying of the relevant VMA information.
Second, it's a text based interface, which makes its programmatic use from
applications and libraries more cumbersome and inefficient due to the
need to handle text parsing to get necessary pieces of information. The
overhead is actually payed both by kernel, formatting originally binary
VMA data into text, and then by user space application, parsing it back
into binary data for further use.
For the on-demand pattern of usage, described above, another problem
when writing generic stack trace symbolization library is an unfortunate
performance-vs-correctness tradeoff that needs to be made. Library has
to make a decision to either cache parsed contents of /proc/<pid>/maps
(after initial processing) to service future requests (if application
requests to symbolize another set of addresses (for the same process),
captured at some later time, which is typical for periodic/continuous
profiling cases) to avoid higher costs of re-parsing this file. Or it
has to choose to cache the contents in memory to speed up future
requests. In the former case, more memory is used for the cache and
there is a risk of getting stale data if application loads or unloads
shared libraries, or otherwise changed its set of VMAs somehow, e.g.,
through additional mmap() calls. In the latter case, it's the
performance hit that comes from re-opening the file and re-parsing its
contents all over again.
This patch aims to solve this problem by providing a new API built on
top of /proc/<pid>/maps. It's meant to address both non-selectiveness
and text nature of /proc/<pid>/maps, by giving user more control of what
sort of VMA(s) needs to be queried, and being binary-based interface
eliminates the overhead of text formatting (on kernel side) and parsing
(on user space side).
It's also designed to be extensible and forward/backward compatible by
including required struct size field, which user has to provide. We use
established copy_struct_from_user() approach to handle extensibility.
User has a choice to pick either getting VMA that covers provided
address or -ENOENT if none is found (exact, least surprising, case). Or,
with an extra query flag (PROCMAP_QUERY_COVERING_OR_NEXT_VMA), they can
get either VMA that covers the address (if there is one), or the closest
next VMA (i.e., VMA with the smallest vm_start > addr). The latter allows
more efficient use, but, given it could be a surprising behavior,
requires an explicit opt-in.
There is another query flag that is useful for some use cases.
PROCMAP_QUERY_FILE_BACKED_VMA instructs this API to only return
file-backed VMAs. Combining this with PROCMAP_QUERY_COVERING_OR_NEXT_VMA
makes it possible to efficiently iterate only file-backed VMAs of the
process, which is what profilers/symbolizers are normally interested in.
All the above querying flags can be combined with (also optional) set of
desired VMA permissions flags. This allows to, for example, iterate only
an executable subset of VMAs, which is what preprocessing pattern, used
by perf tool, would benefit from, as the assumption is that captured
stack traces would have addresses of executable code. This saves time by
skipping non-executable VMAs altogether efficienty.
All these querying flags (modifiers) are orthogonal and can be combined
in a semantically meaningful and natural way.
Basing this ioctl()-based API on top of /proc/<pid>/maps's FD makes
sense given it's querying the same set of VMA data. It's also benefitial
because permission checks for /proc/<pid>/maps is performed at open time
once, and the actual data read of text contents of /proc/<pid>/maps is
done without further permission checks. We piggyback on this pattern
with ioctl()-based API as well, as that's a desired property. Both for
performance reasons, but also for security and flexibility reasons.
Allowing application to open an FD for /proc/self/maps without any extra
capabilities, and then passing it to some sort of profiling agent
through Unix-domain socket, would allow such profiling agent to not
require some of the capabilities that are otherwise expected when
opening /proc/<pid>/maps file for *another* process. This is a desirable
property for some more restricted setups.
This new ioctl-based implementation doesn't interfere with
seq_file-based implementation of /proc/<pid>/maps textual interface, and
so could be used together or independently without paying any price for
that.
Note also, that fetching VMA name (e.g., backing file path, or special
hard-coded or user-provided names) is optional just like build ID. If
user sets vma_name_size to zero, kernel code won't attempt to retrieve
it, saving resources.
Earlier versions of this patch set were adding per-VMA locking, which is
why we have a code structure that is ready for abstracting mmap_lock vs
vm_lock differences (query_vma_setup(), query_vma_teardown(), and
query_vma_find_by_addr()), but given anon_vma_name() is not yet compatible
with per-VMA locking, initial implementation sticks to using only
mmap_lock for now. It will be easy to add back per-VMA locking once all
the pieces are ready later on. Which is why we keep existing code
structure with setup/teardown/query helper functions.
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
fs/proc/task_mmu.c | 235 ++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/fs.h | 130 +++++++++++++++++++++-
2 files changed, 364 insertions(+), 1 deletion(-)
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 507b7dc7c4c8..674405b99d0d 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -375,11 +375,246 @@ static int pid_maps_open(struct inode *inode, struct file *file)
return do_maps_open(inode, file, &proc_pid_maps_op);
}
+#define PROCMAP_QUERY_VMA_FLAGS ( \
+ PROCMAP_QUERY_VMA_READABLE | \
+ PROCMAP_QUERY_VMA_WRITABLE | \
+ PROCMAP_QUERY_VMA_EXECUTABLE | \
+ PROCMAP_QUERY_VMA_SHARED \
+)
+
+#define PROCMAP_QUERY_VALID_FLAGS_MASK ( \
+ PROCMAP_QUERY_COVERING_OR_NEXT_VMA | \
+ PROCMAP_QUERY_FILE_BACKED_VMA | \
+ PROCMAP_QUERY_VMA_FLAGS \
+)
+
+static int query_vma_setup(struct mm_struct *mm)
+{
+ return mmap_read_lock_killable(mm);
+}
+
+static void query_vma_teardown(struct mm_struct *mm, struct vm_area_struct *vma)
+{
+ mmap_read_unlock(mm);
+}
+
+static struct vm_area_struct *query_vma_find_by_addr(struct mm_struct *mm, unsigned long addr)
+{
+ return find_vma(mm, addr);
+}
+
+static struct vm_area_struct *query_matching_vma(struct mm_struct *mm,
+ unsigned long addr, u32 flags)
+{
+ struct vm_area_struct *vma;
+
+next_vma:
+ vma = query_vma_find_by_addr(mm, addr);
+ if (!vma)
+ goto no_vma;
+
+ /* user requested only file-backed VMA, keep iterating */
+ if ((flags & PROCMAP_QUERY_FILE_BACKED_VMA) && !vma->vm_file)
+ goto skip_vma;
+
+ /* VMA permissions should satisfy query flags */
+ if (flags & PROCMAP_QUERY_VMA_FLAGS) {
+ u32 perm = 0;
+
+ if (flags & PROCMAP_QUERY_VMA_READABLE)
+ perm |= VM_READ;
+ if (flags & PROCMAP_QUERY_VMA_WRITABLE)
+ perm |= VM_WRITE;
+ if (flags & PROCMAP_QUERY_VMA_EXECUTABLE)
+ perm |= VM_EXEC;
+ if (flags & PROCMAP_QUERY_VMA_SHARED)
+ perm |= VM_MAYSHARE;
+
+ if ((vma->vm_flags & perm) != perm)
+ goto skip_vma;
+ }
+
+ /* found covering VMA or user is OK with the matching next VMA */
+ if ((flags & PROCMAP_QUERY_COVERING_OR_NEXT_VMA) || vma->vm_start <= addr)
+ return vma;
+
+skip_vma:
+ /*
+ * If the user needs closest matching VMA, keep iterating.
+ */
+ addr = vma->vm_end;
+ if (flags & PROCMAP_QUERY_COVERING_OR_NEXT_VMA)
+ goto next_vma;
+no_vma:
+ return ERR_PTR(-ENOENT);
+}
+
+static int do_procmap_query(struct proc_maps_private *priv, void __user *uarg)
+{
+ struct procmap_query karg;
+ struct vm_area_struct *vma;
+ struct mm_struct *mm;
+ const char *name = NULL;
+ char *name_buf = NULL;
+ __u64 usize;
+ int err;
+
+ if (copy_from_user(&usize, (void __user *)uarg, sizeof(usize)))
+ return -EFAULT;
+ /* argument struct can never be that large, reject abuse */
+ if (usize > PAGE_SIZE)
+ return -E2BIG;
+ /* argument struct should have at least query_flags and query_addr fields */
+ if (usize < offsetofend(struct procmap_query, query_addr))
+ return -EINVAL;
+ err = copy_struct_from_user(&karg, sizeof(karg), uarg, usize);
+ if (err)
+ return err;
+
+ /* reject unknown flags */
+ if (karg.query_flags & ~PROCMAP_QUERY_VALID_FLAGS_MASK)
+ return -EINVAL;
+ /* either both buffer address and size are set, or both should be zero */
+ if (!!karg.vma_name_size != !!karg.vma_name_addr)
+ return -EINVAL;
+
+ mm = priv->mm;
+ if (!mm || !mmget_not_zero(mm))
+ return -ESRCH;
+
+ err = query_vma_setup(mm);
+ if (err) {
+ mmput(mm);
+ return err;
+ }
+
+ vma = query_matching_vma(mm, karg.query_addr, karg.query_flags);
+ if (IS_ERR(vma)) {
+ err = PTR_ERR(vma);
+ vma = NULL;
+ goto out;
+ }
+
+ karg.vma_start = vma->vm_start;
+ karg.vma_end = vma->vm_end;
+
+ karg.vma_flags = 0;
+ if (vma->vm_flags & VM_READ)
+ karg.vma_flags |= PROCMAP_QUERY_VMA_READABLE;
+ if (vma->vm_flags & VM_WRITE)
+ karg.vma_flags |= PROCMAP_QUERY_VMA_WRITABLE;
+ if (vma->vm_flags & VM_EXEC)
+ karg.vma_flags |= PROCMAP_QUERY_VMA_EXECUTABLE;
+ if (vma->vm_flags & VM_MAYSHARE)
+ karg.vma_flags |= PROCMAP_QUERY_VMA_SHARED;
+
+ karg.vma_page_size = vma_kernel_pagesize(vma);
+
+ if (vma->vm_file) {
+ const struct inode *inode = file_user_inode(vma->vm_file);
+
+ karg.vma_offset = ((__u64)vma->vm_pgoff) << PAGE_SHIFT;
+ karg.dev_major = MAJOR(inode->i_sb->s_dev);
+ karg.dev_minor = MINOR(inode->i_sb->s_dev);
+ karg.inode = inode->i_ino;
+ } else {
+ karg.vma_offset = 0;
+ karg.dev_major = 0;
+ karg.dev_minor = 0;
+ karg.inode = 0;
+ }
+
+ if (karg.build_id_size) {
+ __u32 build_id_sz;
+
+ err = build_id_parse(vma, build_id_buf, &build_id_sz);
+ if (err) {
+ karg.build_id_size = 0;
+ } else {
+ if (karg.build_id_size < build_id_sz) {
+ err = -ENAMETOOLONG;
+ goto out;
+ }
+ karg.build_id_size = build_id_sz;
+ }
+ }
+
+ if (karg.vma_name_size) {
+ size_t name_buf_sz = min_t(size_t, PATH_MAX, karg.vma_name_size);
+ const struct path *path;
+ const char *name_fmt;
+ size_t name_sz = 0;
+
+ get_vma_name(vma, &path, &name, &name_fmt);
+
+ if (path || name_fmt || name) {
+ name_buf = kmalloc(name_buf_sz, GFP_KERNEL);
+ if (!name_buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ if (path) {
+ name = d_path(path, name_buf, name_buf_sz);
+ if (IS_ERR(name)) {
+ err = PTR_ERR(name);
+ goto out;
+ }
+ name_sz = name_buf + name_buf_sz - name;
+ } else if (name || name_fmt) {
+ name_sz = 1 + snprintf(name_buf, name_buf_sz, name_fmt ?: "%s", name);
+ name = name_buf;
+ }
+ if (name_sz > name_buf_sz) {
+ err = -ENAMETOOLONG;
+ goto out;
+ }
+ karg.vma_name_size = name_sz;
+ }
+
+ /* unlock vma or mmap_lock, and put mm_struct before copying data to user */
+ query_vma_teardown(mm, vma);
+ mmput(mm);
+
+ if (karg.vma_name_size && copy_to_user((void __user *)karg.vma_name_addr,
+ name, karg.vma_name_size)) {
+ kfree(name_buf);
+ return -EFAULT;
+ }
+ kfree(name_buf);
+
+ if (copy_to_user(uarg, &karg, min_t(size_t, sizeof(karg), usize)))
+ return -EFAULT;
+
+ return 0;
+
+out:
+ query_vma_teardown(mm, vma);
+ mmput(mm);
+ kfree(name_buf);
+ return err;
+}
+
+static long procfs_procmap_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct seq_file *seq = file->private_data;
+ struct proc_maps_private *priv = seq->private;
+
+ switch (cmd) {
+ case PROCMAP_QUERY:
+ return do_procmap_query(priv, (void __user *)arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
const struct file_operations proc_pid_maps_operations = {
.open = pid_maps_open,
.read = seq_read,
.llseek = seq_lseek,
.release = proc_map_release,
+ .unlocked_ioctl = procfs_procmap_ioctl,
+ .compat_ioctl = procfs_procmap_ioctl,
};
/*
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 45e4e64fd664..74480ed4fa78 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -333,8 +333,10 @@ typedef int __bitwise __kernel_rwf_t;
#define RWF_SUPPORTED (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT |\
RWF_APPEND | RWF_NOAPPEND)
+#define PROCFS_IOCTL_MAGIC 'f'
+
/* Pagemap ioctl */
-#define PAGEMAP_SCAN _IOWR('f', 16, struct pm_scan_arg)
+#define PAGEMAP_SCAN _IOWR(PROCFS_IOCTL_MAGIC, 16, struct pm_scan_arg)
/* Bitmasks provided in pm_scan_args masks and reported in page_region.categories. */
#define PAGE_IS_WPALLOWED (1 << 0)
@@ -393,4 +395,130 @@ struct pm_scan_arg {
__u64 return_mask;
};
+/* /proc/<pid>/maps ioctl */
+#define PROCMAP_QUERY _IOWR(PROCFS_IOCTL_MAGIC, 17, struct procmap_query)
+
+enum procmap_query_flags {
+ /*
+ * VMA permission flags.
+ *
+ * Can be used as part of procmap_query.query_flags field to look up
+ * only VMAs satisfying specified subset of permissions. E.g., specifying
+ * PROCMAP_QUERY_VMA_READABLE only will return both readable and read/write VMAs,
+ * while having PROCMAP_QUERY_VMA_READABLE | PROCMAP_QUERY_VMA_WRITABLE will only
+ * return read/write VMAs, though both executable/non-executable and
+ * private/shared will be ignored.
+ *
+ * PROCMAP_QUERY_VMA_* flags are also returned in procmap_query.vma_flags
+ * field to specify actual VMA permissions.
+ */
+ PROCMAP_QUERY_VMA_READABLE = 0x01,
+ PROCMAP_QUERY_VMA_WRITABLE = 0x02,
+ PROCMAP_QUERY_VMA_EXECUTABLE = 0x04,
+ PROCMAP_QUERY_VMA_SHARED = 0x08,
+ /*
+ * Query modifier flags.
+ *
+ * By default VMA that covers provided address is returned, or -ENOENT
+ * is returned. With PROCMAP_QUERY_COVERING_OR_NEXT_VMA flag set, closest
+ * VMA with vma_start > addr will be returned if no covering VMA is
+ * found.
+ *
+ * PROCMAP_QUERY_FILE_BACKED_VMA instructs query to consider only VMAs that
+ * have file backing. Can be combined with PROCMAP_QUERY_COVERING_OR_NEXT_VMA
+ * to iterate all VMAs with file backing.
+ */
+ PROCMAP_QUERY_COVERING_OR_NEXT_VMA = 0x10,
+ PROCMAP_QUERY_FILE_BACKED_VMA = 0x20,
+};
+
+/*
+ * Input/output argument structured passed into ioctl() call. It can be used
+ * to query a set of VMAs (Virtual Memory Areas) of a process.
+ *
+ * Each field can be one of three kinds, marked in a short comment to the
+ * right of the field:
+ * - "in", input argument, user has to provide this value, kernel doesn't modify it;
+ * - "out", output argument, kernel sets this field with VMA data;
+ * - "in/out", input and output argument; user provides initial value (used
+ * to specify maximum allowable buffer size), and kernel sets it to actual
+ * amount of data written (or zero, if there is no data).
+ *
+ * If matching VMA is found (according to criterias specified by
+ * query_addr/query_flags, all the out fields are filled out, and ioctl()
+ * returns 0. If there is no matching VMA, -ENOENT will be returned.
+ * In case of any other error, negative error code other than -ENOENT is
+ * returned.
+ *
+ * Most of the data is similar to the one returned as text in /proc/<pid>/maps
+ * file, but procmap_query provides more querying flexibility. There are no
+ * consistency guarantees between subsequent ioctl() calls, but data returned
+ * for matched VMA is self-consistent.
+ */
+struct procmap_query {
+ /* Query struct size, for backwards/forward compatibility */
+ __u64 size;
+ /*
+ * Query flags, a combination of enum procmap_query_flags values.
+ * Defines query filtering and behavior, see enum procmap_query_flags.
+ *
+ * Input argument, provided by user. Kernel doesn't modify it.
+ */
+ __u64 query_flags; /* in */
+ /*
+ * Query address. By default, VMA that covers this address will
+ * be looked up. PROCMAP_QUERY_* flags above modify this default
+ * behavior further.
+ *
+ * Input argument, provided by user. Kernel doesn't modify it.
+ */
+ __u64 query_addr; /* in */
+ /* VMA starting (inclusive) and ending (exclusive) address, if VMA is found. */
+ __u64 vma_start; /* out */
+ __u64 vma_end; /* out */
+ /* VMA permissions flags. A combination of PROCMAP_QUERY_VMA_* flags. */
+ __u64 vma_flags; /* out */
+ /* VMA backing page size granularity. */
+ __u32 vma_page_size; /* out */
+ /*
+ * VMA file offset. If VMA has file backing, this specifies offset
+ * within the file that VMA's start address corresponds to.
+ * Is set to zero if VMA has no backing file.
+ */
+ __u64 vma_offset; /* out */
+ /* Backing file's inode number, or zero, if VMA has no backing file. */
+ __u64 inode; /* out */
+ /* Backing file's device major/minor number, or zero, if VMA has no backing file. */
+ __u32 dev_major; /* out */
+ __u32 dev_minor; /* out */
+ /*
+ * If set to non-zero value, signals the request to return VMA name
+ * (i.e., VMA's backing file's absolute path, with " (deleted)" suffix
+ * appended, if file was unlinked from FS) for matched VMA. VMA name
+ * can also be some special name (e.g., "[heap]", "[stack]") or could
+ * be even user-supplied with prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME).
+ *
+ * Kernel will set this field to zero, if VMA has no associated name.
+ * Otherwise kernel will return actual amount of bytes filled in
+ * user-supplied buffer (see vma_name_addr field below), including the
+ * terminating zero.
+ *
+ * If VMA name is longer that user-supplied maximum buffer size,
+ * -E2BIG error is returned.
+ *
+ * If this field is set to non-zero value, vma_name_addr should point
+ * to valid user space memory buffer of at least vma_name_size bytes.
+ * If set to zero, vma_name_addr should be set to zero as well
+ */
+ __u32 vma_name_size; /* in/out */
+ /*
+ * User-supplied address of a buffer of at least vma_name_size bytes
+ * for kernel to fill with matched VMA's name (see vma_name_size field
+ * description above for details).
+ *
+ * Should be set to zero if VMA name should not be returned.
+ */
+ __u64 vma_name_addr; /* in */
+};
+
#endif /* _UAPI_LINUX_FS_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v5 2/6] fs/procfs: implement efficient VMA querying API for /proc/<pid>/maps
2024-06-18 22:45 ` [PATCH v5 2/6] fs/procfs: implement efficient VMA querying API for /proc/<pid>/maps Andrii Nakryiko
@ 2024-06-26 2:42 ` Liam R. Howlett
2024-06-26 16:37 ` Andrii Nakryiko
0 siblings, 1 reply; 11+ messages in thread
From: Liam R. Howlett @ 2024-06-26 2:42 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: linux-fsdevel, brauner, viro, akpm, linux-kernel, bpf, gregkh,
linux-mm, surenb, rppt, adobriyan
* Andrii Nakryiko <andrii@kernel.org> [240618 18:45]:
...
> +
> +static int do_procmap_query(struct proc_maps_private *priv, void __user *uarg)
> +{
> + struct procmap_query karg;
> + struct vm_area_struct *vma;
> + struct mm_struct *mm;
> + const char *name = NULL;
> + char *name_buf = NULL;
> + __u64 usize;
> + int err;
> +
> + if (copy_from_user(&usize, (void __user *)uarg, sizeof(usize)))
> + return -EFAULT;
> + /* argument struct can never be that large, reject abuse */
> + if (usize > PAGE_SIZE)
> + return -E2BIG;
> + /* argument struct should have at least query_flags and query_addr fields */
> + if (usize < offsetofend(struct procmap_query, query_addr))
> + return -EINVAL;
> + err = copy_struct_from_user(&karg, sizeof(karg), uarg, usize);
> + if (err)
> + return err;
> +
> + /* reject unknown flags */
> + if (karg.query_flags & ~PROCMAP_QUERY_VALID_FLAGS_MASK)
> + return -EINVAL;
> + /* either both buffer address and size are set, or both should be zero */
> + if (!!karg.vma_name_size != !!karg.vma_name_addr)
> + return -EINVAL;
> +
> + mm = priv->mm;
> + if (!mm || !mmget_not_zero(mm))
> + return -ESRCH;
> +
> + err = query_vma_setup(mm);
> + if (err) {
> + mmput(mm);
> + return err;
> + }
> +
> + vma = query_matching_vma(mm, karg.query_addr, karg.query_flags);
> + if (IS_ERR(vma)) {
> + err = PTR_ERR(vma);
> + vma = NULL;
> + goto out;
> + }
> +
> + karg.vma_start = vma->vm_start;
> + karg.vma_end = vma->vm_end;
> +
> + karg.vma_flags = 0;
> + if (vma->vm_flags & VM_READ)
> + karg.vma_flags |= PROCMAP_QUERY_VMA_READABLE;
> + if (vma->vm_flags & VM_WRITE)
> + karg.vma_flags |= PROCMAP_QUERY_VMA_WRITABLE;
> + if (vma->vm_flags & VM_EXEC)
> + karg.vma_flags |= PROCMAP_QUERY_VMA_EXECUTABLE;
> + if (vma->vm_flags & VM_MAYSHARE)
> + karg.vma_flags |= PROCMAP_QUERY_VMA_SHARED;
> +
> + karg.vma_page_size = vma_kernel_pagesize(vma);
> +
...
> +/*
> + * Input/output argument structured passed into ioctl() call. It can be used
> + * to query a set of VMAs (Virtual Memory Areas) of a process.
> + *
> + * Each field can be one of three kinds, marked in a short comment to the
> + * right of the field:
> + * - "in", input argument, user has to provide this value, kernel doesn't modify it;
> + * - "out", output argument, kernel sets this field with VMA data;
> + * - "in/out", input and output argument; user provides initial value (used
> + * to specify maximum allowable buffer size), and kernel sets it to actual
> + * amount of data written (or zero, if there is no data).
> + *
> + * If matching VMA is found (according to criterias specified by
> + * query_addr/query_flags, all the out fields are filled out, and ioctl()
> + * returns 0. If there is no matching VMA, -ENOENT will be returned.
> + * In case of any other error, negative error code other than -ENOENT is
> + * returned.
> + *
> + * Most of the data is similar to the one returned as text in /proc/<pid>/maps
> + * file, but procmap_query provides more querying flexibility. There are no
> + * consistency guarantees between subsequent ioctl() calls, but data returned
> + * for matched VMA is self-consistent.
> + */
> +struct procmap_query {
> + /* Query struct size, for backwards/forward compatibility */
> + __u64 size;
> + /*
> + * Query flags, a combination of enum procmap_query_flags values.
> + * Defines query filtering and behavior, see enum procmap_query_flags.
> + *
> + * Input argument, provided by user. Kernel doesn't modify it.
> + */
> + __u64 query_flags; /* in */
> + /*
> + * Query address. By default, VMA that covers this address will
> + * be looked up. PROCMAP_QUERY_* flags above modify this default
> + * behavior further.
> + *
> + * Input argument, provided by user. Kernel doesn't modify it.
> + */
> + __u64 query_addr; /* in */
> + /* VMA starting (inclusive) and ending (exclusive) address, if VMA is found. */
> + __u64 vma_start; /* out */
> + __u64 vma_end; /* out */
> + /* VMA permissions flags. A combination of PROCMAP_QUERY_VMA_* flags. */
> + __u64 vma_flags; /* out */
> + /* VMA backing page size granularity. */
> + __u32 vma_page_size; /* out */
The vma_kernel_pagesize() returns an unsigned long. We could
potentially be truncating the returned value (although probably not
today?). This is from the vm_operations_struct pagesize, which also
returns an unsigned long. Could we switch this to __u64?
...
Thanks,
Liam
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 2/6] fs/procfs: implement efficient VMA querying API for /proc/<pid>/maps
2024-06-26 2:42 ` Liam R. Howlett
@ 2024-06-26 16:37 ` Andrii Nakryiko
0 siblings, 0 replies; 11+ messages in thread
From: Andrii Nakryiko @ 2024-06-26 16:37 UTC (permalink / raw)
To: Liam R. Howlett, Andrii Nakryiko, linux-fsdevel, brauner, viro,
akpm, linux-kernel, bpf, gregkh, linux-mm, surenb, rppt,
adobriyan
On Tue, Jun 25, 2024 at 7:42 PM Liam R. Howlett <Liam.Howlett@oracle.com> wrote:
>
> * Andrii Nakryiko <andrii@kernel.org> [240618 18:45]:
> ...
>
> > +
> > +static int do_procmap_query(struct proc_maps_private *priv, void __user *uarg)
> > +{
> > + struct procmap_query karg;
> > + struct vm_area_struct *vma;
> > + struct mm_struct *mm;
> > + const char *name = NULL;
> > + char *name_buf = NULL;
> > + __u64 usize;
> > + int err;
> > +
> > + if (copy_from_user(&usize, (void __user *)uarg, sizeof(usize)))
> > + return -EFAULT;
> > + /* argument struct can never be that large, reject abuse */
> > + if (usize > PAGE_SIZE)
> > + return -E2BIG;
> > + /* argument struct should have at least query_flags and query_addr fields */
> > + if (usize < offsetofend(struct procmap_query, query_addr))
> > + return -EINVAL;
> > + err = copy_struct_from_user(&karg, sizeof(karg), uarg, usize);
> > + if (err)
> > + return err;
> > +
> > + /* reject unknown flags */
> > + if (karg.query_flags & ~PROCMAP_QUERY_VALID_FLAGS_MASK)
> > + return -EINVAL;
> > + /* either both buffer address and size are set, or both should be zero */
> > + if (!!karg.vma_name_size != !!karg.vma_name_addr)
> > + return -EINVAL;
> > +
> > + mm = priv->mm;
> > + if (!mm || !mmget_not_zero(mm))
> > + return -ESRCH;
> > +
> > + err = query_vma_setup(mm);
> > + if (err) {
> > + mmput(mm);
> > + return err;
> > + }
> > +
> > + vma = query_matching_vma(mm, karg.query_addr, karg.query_flags);
> > + if (IS_ERR(vma)) {
> > + err = PTR_ERR(vma);
> > + vma = NULL;
> > + goto out;
> > + }
> > +
> > + karg.vma_start = vma->vm_start;
> > + karg.vma_end = vma->vm_end;
> > +
> > + karg.vma_flags = 0;
> > + if (vma->vm_flags & VM_READ)
> > + karg.vma_flags |= PROCMAP_QUERY_VMA_READABLE;
> > + if (vma->vm_flags & VM_WRITE)
> > + karg.vma_flags |= PROCMAP_QUERY_VMA_WRITABLE;
> > + if (vma->vm_flags & VM_EXEC)
> > + karg.vma_flags |= PROCMAP_QUERY_VMA_EXECUTABLE;
> > + if (vma->vm_flags & VM_MAYSHARE)
> > + karg.vma_flags |= PROCMAP_QUERY_VMA_SHARED;
> > +
> > + karg.vma_page_size = vma_kernel_pagesize(vma);
> > +
> ...
>
> > +/*
> > + * Input/output argument structured passed into ioctl() call. It can be used
> > + * to query a set of VMAs (Virtual Memory Areas) of a process.
> > + *
> > + * Each field can be one of three kinds, marked in a short comment to the
> > + * right of the field:
> > + * - "in", input argument, user has to provide this value, kernel doesn't modify it;
> > + * - "out", output argument, kernel sets this field with VMA data;
> > + * - "in/out", input and output argument; user provides initial value (used
> > + * to specify maximum allowable buffer size), and kernel sets it to actual
> > + * amount of data written (or zero, if there is no data).
> > + *
> > + * If matching VMA is found (according to criterias specified by
> > + * query_addr/query_flags, all the out fields are filled out, and ioctl()
> > + * returns 0. If there is no matching VMA, -ENOENT will be returned.
> > + * In case of any other error, negative error code other than -ENOENT is
> > + * returned.
> > + *
> > + * Most of the data is similar to the one returned as text in /proc/<pid>/maps
> > + * file, but procmap_query provides more querying flexibility. There are no
> > + * consistency guarantees between subsequent ioctl() calls, but data returned
> > + * for matched VMA is self-consistent.
> > + */
> > +struct procmap_query {
> > + /* Query struct size, for backwards/forward compatibility */
> > + __u64 size;
> > + /*
> > + * Query flags, a combination of enum procmap_query_flags values.
> > + * Defines query filtering and behavior, see enum procmap_query_flags.
> > + *
> > + * Input argument, provided by user. Kernel doesn't modify it.
> > + */
> > + __u64 query_flags; /* in */
> > + /*
> > + * Query address. By default, VMA that covers this address will
> > + * be looked up. PROCMAP_QUERY_* flags above modify this default
> > + * behavior further.
> > + *
> > + * Input argument, provided by user. Kernel doesn't modify it.
> > + */
> > + __u64 query_addr; /* in */
> > + /* VMA starting (inclusive) and ending (exclusive) address, if VMA is found. */
> > + __u64 vma_start; /* out */
> > + __u64 vma_end; /* out */
> > + /* VMA permissions flags. A combination of PROCMAP_QUERY_VMA_* flags. */
> > + __u64 vma_flags; /* out */
> > + /* VMA backing page size granularity. */
> > + __u32 vma_page_size; /* out */
>
> The vma_kernel_pagesize() returns an unsigned long. We could
> potentially be truncating the returned value (although probably not
> today?). This is from the vm_operations_struct pagesize, which also
> returns an unsigned long. Could we switch this to __u64?
>
Yep, fair point. It seemed excessive to use u64, but I guess nothing
prevents us (in the future) from having humongous pages with >4GB
sizes. I'll update to __u64.
I'll wait for a few more hours just in case I get some other feedback,
and will rebase and post a new revision later today, thanks.
> ...
>
> Thanks,
> Liam
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 3/6] fs/procfs: add build ID fetching to PROCMAP_QUERY API
2024-06-18 22:45 [PATCH v5 0/6] ioctl()-based API to query VMAs from /proc/<pid>/maps Andrii Nakryiko
2024-06-18 22:45 ` [PATCH v5 1/6] fs/procfs: extract logic for getting VMA name constituents Andrii Nakryiko
2024-06-18 22:45 ` [PATCH v5 2/6] fs/procfs: implement efficient VMA querying API for /proc/<pid>/maps Andrii Nakryiko
@ 2024-06-18 22:45 ` Andrii Nakryiko
2024-06-19 10:14 ` Alexey Dobriyan
2024-06-18 22:45 ` [PATCH v5 4/6] docs/procfs: call out ioctl()-based PROCMAP_QUERY command existence Andrii Nakryiko
` (2 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Andrii Nakryiko @ 2024-06-18 22:45 UTC (permalink / raw)
To: linux-fsdevel, brauner, viro, akpm
Cc: linux-kernel, bpf, gregkh, linux-mm, liam.howlett, surenb, rppt,
adobriyan, Andrii Nakryiko
The need to get ELF build ID reliably is an important aspect when
dealing with profiling and stack trace symbolization, and
/proc/<pid>/maps textual representation doesn't help with this.
To get backing file's ELF build ID, application has to first resolve
VMA, then use it's start/end address range to follow a special
/proc/<pid>/map_files/<start>-<end> symlink to open the ELF file (this
is necessary because backing file might have been removed from the disk
or was already replaced with another binary in the same file path.
Such approach, beyond just adding complexity of having to do a bunch of
extra work, has extra security implications. Because application opens
underlying ELF file and needs read access to its entire contents (as far
as kernel is concerned), kernel puts additional capable() checks on
following /proc/<pid>/map_files/<start>-<end> symlink. And that makes
sense in general.
But in the case of build ID, profiler/symbolizer doesn't need the
contents of ELF file, per se. It's only build ID that is of interest,
and ELF build ID itself doesn't provide any sensitive information.
So this patch adds a way to request backing file's ELF build ID along
the rest of VMA information in the same API. User has control over
whether this piece of information is requested or not by either setting
build_id_size field to zero or non-zero maximum buffer size they
provided through build_id_addr field (which encodes user pointer as
__u64 field). This is a completely optional piece of information, and so
has no performance implications for user cases that don't care about
build ID, while improving performance and simplifying the setup for
those application that do need it.
Kernel already implements build ID fetching, which is used from BPF
subsystem. We are reusing this code here, but plan a follow up changes
to make it work better under more relaxed assumption (compared to what
existing code assumes) of being called from user process context, in
which page faults are allowed. BPF-specific implementation currently
bails out if necessary part of ELF file is not paged in, all due to
extra BPF-specific restrictions (like the need to fetch build ID in
restrictive contexts such as NMI handler).
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
fs/proc/task_mmu.c | 25 ++++++++++++++++++++++++-
include/uapi/linux/fs.h | 28 ++++++++++++++++++++++++++++
2 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 674405b99d0d..32bef3eeab7f 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -22,6 +22,7 @@
#include <linux/pkeys.h>
#include <linux/minmax.h>
#include <linux/overflow.h>
+#include <linux/buildid.h>
#include <asm/elf.h>
#include <asm/tlb.h>
@@ -445,6 +446,7 @@ static struct vm_area_struct *query_matching_vma(struct mm_struct *mm,
addr = vma->vm_end;
if (flags & PROCMAP_QUERY_COVERING_OR_NEXT_VMA)
goto next_vma;
+
no_vma:
return ERR_PTR(-ENOENT);
}
@@ -455,7 +457,7 @@ static int do_procmap_query(struct proc_maps_private *priv, void __user *uarg)
struct vm_area_struct *vma;
struct mm_struct *mm;
const char *name = NULL;
- char *name_buf = NULL;
+ char build_id_buf[BUILD_ID_SIZE_MAX], *name_buf = NULL;
__u64 usize;
int err;
@@ -477,6 +479,8 @@ static int do_procmap_query(struct proc_maps_private *priv, void __user *uarg)
/* either both buffer address and size are set, or both should be zero */
if (!!karg.vma_name_size != !!karg.vma_name_addr)
return -EINVAL;
+ if (!!karg.build_id_size != !!karg.build_id_addr)
+ return -EINVAL;
mm = priv->mm;
if (!mm || !mmget_not_zero(mm))
@@ -539,6 +543,21 @@ static int do_procmap_query(struct proc_maps_private *priv, void __user *uarg)
}
}
+ if (karg.build_id_size) {
+ __u32 build_id_sz;
+
+ err = build_id_parse(vma, build_id_buf, &build_id_sz);
+ if (err) {
+ karg.build_id_size = 0;
+ } else {
+ if (karg.build_id_size < build_id_sz) {
+ err = -ENAMETOOLONG;
+ goto out;
+ }
+ karg.build_id_size = build_id_sz;
+ }
+ }
+
if (karg.vma_name_size) {
size_t name_buf_sz = min_t(size_t, PATH_MAX, karg.vma_name_size);
const struct path *path;
@@ -583,6 +602,10 @@ static int do_procmap_query(struct proc_maps_private *priv, void __user *uarg)
}
kfree(name_buf);
+ if (karg.build_id_size && copy_to_user((void __user *)karg.build_id_addr,
+ build_id_buf, karg.build_id_size))
+ return -EFAULT;
+
if (copy_to_user(uarg, &karg, min_t(size_t, sizeof(karg), usize)))
return -EFAULT;
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 74480ed4fa78..cad6375044bc 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -511,6 +511,26 @@ struct procmap_query {
* If set to zero, vma_name_addr should be set to zero as well
*/
__u32 vma_name_size; /* in/out */
+ /*
+ * If set to non-zero value, signals the request to extract and return
+ * VMA's backing file's build ID, if the backing file is an ELF file
+ * and it contains embedded build ID.
+ *
+ * Kernel will set this field to zero, if VMA has no backing file,
+ * backing file is not an ELF file, or ELF file has no build ID
+ * embedded.
+ *
+ * Build ID is a binary value (not a string). Kernel will set
+ * build_id_size field to exact number of bytes used for build ID.
+ * If build ID is requested and present, but needs more bytes than
+ * user-supplied maximum buffer size (see build_id_addr field below),
+ * -E2BIG error will be returned.
+ *
+ * If this field is set to non-zero value, build_id_addr should point
+ * to valid user space memory buffer of at least build_id_size bytes.
+ * If set to zero, build_id_addr should be set to zero as well
+ */
+ __u32 build_id_size; /* in/out */
/*
* User-supplied address of a buffer of at least vma_name_size bytes
* for kernel to fill with matched VMA's name (see vma_name_size field
@@ -519,6 +539,14 @@ struct procmap_query {
* Should be set to zero if VMA name should not be returned.
*/
__u64 vma_name_addr; /* in */
+ /*
+ * User-supplied address of a buffer of at least build_id_size bytes
+ * for kernel to fill with matched VMA's ELF build ID, if available
+ * (see build_id_size field description above for details).
+ *
+ * Should be set to zero if build ID should not be returned.
+ */
+ __u64 build_id_addr; /* in */
};
#endif /* _UAPI_LINUX_FS_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v5 3/6] fs/procfs: add build ID fetching to PROCMAP_QUERY API
2024-06-18 22:45 ` [PATCH v5 3/6] fs/procfs: add build ID fetching to PROCMAP_QUERY API Andrii Nakryiko
@ 2024-06-19 10:14 ` Alexey Dobriyan
2024-06-20 18:50 ` Andrii Nakryiko
0 siblings, 1 reply; 11+ messages in thread
From: Alexey Dobriyan @ 2024-06-19 10:14 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: linux-fsdevel, brauner, viro, akpm, linux-kernel, bpf, gregkh,
linux-mm, liam.howlett, surenb, rppt
On Tue, Jun 18, 2024 at 03:45:22PM -0700, Andrii Nakryiko wrote:
> The need to get ELF build ID reliably is an important aspect when
> dealing with profiling and stack trace symbolization, and
> /proc/<pid>/maps textual representation doesn't help with this.
> @@ -539,6 +543,21 @@ static int do_procmap_query(struct proc_maps_private *priv, void __user *uarg)
> }
> }
>
> + if (karg.build_id_size) {
> + __u32 build_id_sz;
> +
> + err = build_id_parse(vma, build_id_buf, &build_id_sz);
This is not your bug but build_id_parse() assumes program headers
immediately follow ELF header which is not guaranteed.
> + * If this field is set to non-zero value, build_id_addr should point
> + * to valid user space memory buffer of at least build_id_size bytes.
> + * If set to zero, build_id_addr should be set to zero as well
> + */
> + __u32 build_id_size; /* in/out */
> /*
> * User-supplied address of a buffer of at least vma_name_size bytes
> * for kernel to fill with matched VMA's name (see vma_name_size field
> @@ -519,6 +539,14 @@ struct procmap_query {
> * Should be set to zero if VMA name should not be returned.
> */
> __u64 vma_name_addr; /* in */
> + /*
> + * User-supplied address of a buffer of at least build_id_size bytes
> + * for kernel to fill with matched VMA's ELF build ID, if available
> + * (see build_id_size field description above for details).
> + *
> + * Should be set to zero if build ID should not be returned.
> + */
> + __u64 build_id_addr; /* in */
Can this be simplified to 512-bit buffer in ioctl structure?
BUILD_ID_SIZE_MAX is 20 which is sha1.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 3/6] fs/procfs: add build ID fetching to PROCMAP_QUERY API
2024-06-19 10:14 ` Alexey Dobriyan
@ 2024-06-20 18:50 ` Andrii Nakryiko
0 siblings, 0 replies; 11+ messages in thread
From: Andrii Nakryiko @ 2024-06-20 18:50 UTC (permalink / raw)
To: Alexey Dobriyan
Cc: Andrii Nakryiko, linux-fsdevel, brauner, viro, akpm, linux-kernel,
bpf, gregkh, linux-mm, liam.howlett, surenb, rppt
On Wed, Jun 19, 2024 at 3:14 AM Alexey Dobriyan <adobriyan@gmail.com> wrote:
>
> On Tue, Jun 18, 2024 at 03:45:22PM -0700, Andrii Nakryiko wrote:
> > The need to get ELF build ID reliably is an important aspect when
> > dealing with profiling and stack trace symbolization, and
> > /proc/<pid>/maps textual representation doesn't help with this.
>
> > @@ -539,6 +543,21 @@ static int do_procmap_query(struct proc_maps_private *priv, void __user *uarg)
> > }
> > }
> >
> > + if (karg.build_id_size) {
> > + __u32 build_id_sz;
> > +
> > + err = build_id_parse(vma, build_id_buf, &build_id_sz);
>
> This is not your bug but build_id_parse() assumes program headers
> immediately follow ELF header which is not guaranteed.
Yes, I'm aware, and I think I stated somewhere that I want to
fix/improve that. The thing is, current build_id_parse() was built for
BPF under NMI context assumption, which is why it can't page in memory
and so on (and this "build ID has to be in the first page" was a
surprise to me, but probably just a technical shortcut to make it a
bit easier to implement). Regardless, my plan, once this API is
merged, is to follow up with make build_id_parse() variant that would
work reliably under sleepable context assumptions. Hopefully that's ok
not to bundle all that with these patches?
>
> > + * If this field is set to non-zero value, build_id_addr should point
> > + * to valid user space memory buffer of at least build_id_size bytes.
> > + * If set to zero, build_id_addr should be set to zero as well
> > + */
> > + __u32 build_id_size; /* in/out */
> > /*
> > * User-supplied address of a buffer of at least vma_name_size bytes
> > * for kernel to fill with matched VMA's name (see vma_name_size field
> > @@ -519,6 +539,14 @@ struct procmap_query {
> > * Should be set to zero if VMA name should not be returned.
> > */
> > __u64 vma_name_addr; /* in */
> > + /*
> > + * User-supplied address of a buffer of at least build_id_size bytes
> > + * for kernel to fill with matched VMA's ELF build ID, if available
> > + * (see build_id_size field description above for details).
> > + *
> > + * Should be set to zero if build ID should not be returned.
> > + */
> > + __u64 build_id_addr; /* in */
>
> Can this be simplified to 512-bit buffer in ioctl structure?
> BUILD_ID_SIZE_MAX is 20 which is sha1.
I'd prefer not to because vma_name can't use the same trick, so we'll
have to have this size+buffer address approach anyway. And because of
that I'd like to have all these optional variable-length/string output
arguments handled in a uniform way. In practice, it's really simple to
use this from user-space, the only mildly annoying part is casting
pointer to __u64. But as I said, for vma_name users will do this
anyways, so not much benefit simplifying the build_id part only.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 4/6] docs/procfs: call out ioctl()-based PROCMAP_QUERY command existence
2024-06-18 22:45 [PATCH v5 0/6] ioctl()-based API to query VMAs from /proc/<pid>/maps Andrii Nakryiko
` (2 preceding siblings ...)
2024-06-18 22:45 ` [PATCH v5 3/6] fs/procfs: add build ID fetching to PROCMAP_QUERY API Andrii Nakryiko
@ 2024-06-18 22:45 ` Andrii Nakryiko
2024-06-18 22:45 ` [PATCH v5 5/6] tools: sync uapi/linux/fs.h header into tools subdir Andrii Nakryiko
2024-06-18 22:45 ` [PATCH v5 6/6] selftests/proc: add PROCMAP_QUERY ioctl tests Andrii Nakryiko
5 siblings, 0 replies; 11+ messages in thread
From: Andrii Nakryiko @ 2024-06-18 22:45 UTC (permalink / raw)
To: linux-fsdevel, brauner, viro, akpm
Cc: linux-kernel, bpf, gregkh, linux-mm, liam.howlett, surenb, rppt,
adobriyan, Andrii Nakryiko
Call out PROCMAP_QUERY ioctl() existence in the section describing
/proc/PID/maps file in documentation. We refer user to UAPI header for
low-level details of this programmatic interface.
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
Documentation/filesystems/proc.rst | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst
index 82d142de3461..e834779d9611 100644
--- a/Documentation/filesystems/proc.rst
+++ b/Documentation/filesystems/proc.rst
@@ -443,6 +443,15 @@ is not associated with a file:
or if empty, the mapping is anonymous.
+Starting with 6.11 kernel, /proc/PID/maps provides an alternative
+ioctl()-based API that gives ability to flexibly and efficiently query and
+filter individual VMAs. This interface is binary and is meant for more
+efficient and easy programmatic use. `struct procmap_query`, defined in
+linux/fs.h UAPI header, serves as an input/output argument to the
+`PROCMAP_QUERY` ioctl() command. See comments in linus/fs.h UAPI header for
+details on query semantics, supported flags, data returned, and general API
+usage information.
+
The /proc/PID/smaps is an extension based on maps, showing the memory
consumption for each of the process's mappings. For each mapping (aka Virtual
Memory Area, or VMA) there is a series of lines such as the following::
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 5/6] tools: sync uapi/linux/fs.h header into tools subdir
2024-06-18 22:45 [PATCH v5 0/6] ioctl()-based API to query VMAs from /proc/<pid>/maps Andrii Nakryiko
` (3 preceding siblings ...)
2024-06-18 22:45 ` [PATCH v5 4/6] docs/procfs: call out ioctl()-based PROCMAP_QUERY command existence Andrii Nakryiko
@ 2024-06-18 22:45 ` Andrii Nakryiko
2024-06-18 22:45 ` [PATCH v5 6/6] selftests/proc: add PROCMAP_QUERY ioctl tests Andrii Nakryiko
5 siblings, 0 replies; 11+ messages in thread
From: Andrii Nakryiko @ 2024-06-18 22:45 UTC (permalink / raw)
To: linux-fsdevel, brauner, viro, akpm
Cc: linux-kernel, bpf, gregkh, linux-mm, liam.howlett, surenb, rppt,
adobriyan, Andrii Nakryiko
We need this UAPI header in tools/include subdirectory for using it from
BPF selftests.
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
tools/include/uapi/linux/fs.h | 184 +++++++++++++++++++++++++++++++---
1 file changed, 172 insertions(+), 12 deletions(-)
diff --git a/tools/include/uapi/linux/fs.h b/tools/include/uapi/linux/fs.h
index cc3fea99fd43..cad6375044bc 100644
--- a/tools/include/uapi/linux/fs.h
+++ b/tools/include/uapi/linux/fs.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_FS_H
-#define _LINUX_FS_H
+#ifndef _UAPI_LINUX_FS_H
+#define _UAPI_LINUX_FS_H
/*
* This file has definitions for some important file table structures
@@ -13,10 +13,14 @@
#include <linux/limits.h>
#include <linux/ioctl.h>
#include <linux/types.h>
+#ifndef __KERNEL__
#include <linux/fscrypt.h>
+#endif
/* Use of MS_* flags within the kernel is restricted to core mount(2) code. */
+#if !defined(__KERNEL__)
#include <linux/mount.h>
+#endif
/*
* It's silly to have NR_OPEN bigger than NR_FILE, but you can change
@@ -24,8 +28,8 @@
* nr_file rlimit, so it's safe to set up a ridiculously high absolute
* upper limit on files-per-process.
*
- * Some programs (notably those using select()) may have to be
- * recompiled to take full advantage of the new limits..
+ * Some programs (notably those using select()) may have to be
+ * recompiled to take full advantage of the new limits..
*/
/* Fixed constants first: */
@@ -308,29 +312,31 @@ struct fsxattr {
typedef int __bitwise __kernel_rwf_t;
/* high priority request, poll if possible */
-#define RWF_HIPRI ((__kernel_rwf_t)0x00000001)
+#define RWF_HIPRI ((__force __kernel_rwf_t)0x00000001)
/* per-IO O_DSYNC */
-#define RWF_DSYNC ((__kernel_rwf_t)0x00000002)
+#define RWF_DSYNC ((__force __kernel_rwf_t)0x00000002)
/* per-IO O_SYNC */
-#define RWF_SYNC ((__kernel_rwf_t)0x00000004)
+#define RWF_SYNC ((__force __kernel_rwf_t)0x00000004)
/* per-IO, return -EAGAIN if operation would block */
-#define RWF_NOWAIT ((__kernel_rwf_t)0x00000008)
+#define RWF_NOWAIT ((__force __kernel_rwf_t)0x00000008)
/* per-IO O_APPEND */
-#define RWF_APPEND ((__kernel_rwf_t)0x00000010)
+#define RWF_APPEND ((__force __kernel_rwf_t)0x00000010)
/* per-IO negation of O_APPEND */
-#define RWF_NOAPPEND ((__kernel_rwf_t)0x00000020)
+#define RWF_NOAPPEND ((__force __kernel_rwf_t)0x00000020)
/* mask of flags supported by the kernel */
#define RWF_SUPPORTED (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT |\
RWF_APPEND | RWF_NOAPPEND)
+#define PROCFS_IOCTL_MAGIC 'f'
+
/* Pagemap ioctl */
-#define PAGEMAP_SCAN _IOWR('f', 16, struct pm_scan_arg)
+#define PAGEMAP_SCAN _IOWR(PROCFS_IOCTL_MAGIC, 16, struct pm_scan_arg)
/* Bitmasks provided in pm_scan_args masks and reported in page_region.categories. */
#define PAGE_IS_WPALLOWED (1 << 0)
@@ -389,4 +395,158 @@ struct pm_scan_arg {
__u64 return_mask;
};
-#endif /* _LINUX_FS_H */
+/* /proc/<pid>/maps ioctl */
+#define PROCMAP_QUERY _IOWR(PROCFS_IOCTL_MAGIC, 17, struct procmap_query)
+
+enum procmap_query_flags {
+ /*
+ * VMA permission flags.
+ *
+ * Can be used as part of procmap_query.query_flags field to look up
+ * only VMAs satisfying specified subset of permissions. E.g., specifying
+ * PROCMAP_QUERY_VMA_READABLE only will return both readable and read/write VMAs,
+ * while having PROCMAP_QUERY_VMA_READABLE | PROCMAP_QUERY_VMA_WRITABLE will only
+ * return read/write VMAs, though both executable/non-executable and
+ * private/shared will be ignored.
+ *
+ * PROCMAP_QUERY_VMA_* flags are also returned in procmap_query.vma_flags
+ * field to specify actual VMA permissions.
+ */
+ PROCMAP_QUERY_VMA_READABLE = 0x01,
+ PROCMAP_QUERY_VMA_WRITABLE = 0x02,
+ PROCMAP_QUERY_VMA_EXECUTABLE = 0x04,
+ PROCMAP_QUERY_VMA_SHARED = 0x08,
+ /*
+ * Query modifier flags.
+ *
+ * By default VMA that covers provided address is returned, or -ENOENT
+ * is returned. With PROCMAP_QUERY_COVERING_OR_NEXT_VMA flag set, closest
+ * VMA with vma_start > addr will be returned if no covering VMA is
+ * found.
+ *
+ * PROCMAP_QUERY_FILE_BACKED_VMA instructs query to consider only VMAs that
+ * have file backing. Can be combined with PROCMAP_QUERY_COVERING_OR_NEXT_VMA
+ * to iterate all VMAs with file backing.
+ */
+ PROCMAP_QUERY_COVERING_OR_NEXT_VMA = 0x10,
+ PROCMAP_QUERY_FILE_BACKED_VMA = 0x20,
+};
+
+/*
+ * Input/output argument structured passed into ioctl() call. It can be used
+ * to query a set of VMAs (Virtual Memory Areas) of a process.
+ *
+ * Each field can be one of three kinds, marked in a short comment to the
+ * right of the field:
+ * - "in", input argument, user has to provide this value, kernel doesn't modify it;
+ * - "out", output argument, kernel sets this field with VMA data;
+ * - "in/out", input and output argument; user provides initial value (used
+ * to specify maximum allowable buffer size), and kernel sets it to actual
+ * amount of data written (or zero, if there is no data).
+ *
+ * If matching VMA is found (according to criterias specified by
+ * query_addr/query_flags, all the out fields are filled out, and ioctl()
+ * returns 0. If there is no matching VMA, -ENOENT will be returned.
+ * In case of any other error, negative error code other than -ENOENT is
+ * returned.
+ *
+ * Most of the data is similar to the one returned as text in /proc/<pid>/maps
+ * file, but procmap_query provides more querying flexibility. There are no
+ * consistency guarantees between subsequent ioctl() calls, but data returned
+ * for matched VMA is self-consistent.
+ */
+struct procmap_query {
+ /* Query struct size, for backwards/forward compatibility */
+ __u64 size;
+ /*
+ * Query flags, a combination of enum procmap_query_flags values.
+ * Defines query filtering and behavior, see enum procmap_query_flags.
+ *
+ * Input argument, provided by user. Kernel doesn't modify it.
+ */
+ __u64 query_flags; /* in */
+ /*
+ * Query address. By default, VMA that covers this address will
+ * be looked up. PROCMAP_QUERY_* flags above modify this default
+ * behavior further.
+ *
+ * Input argument, provided by user. Kernel doesn't modify it.
+ */
+ __u64 query_addr; /* in */
+ /* VMA starting (inclusive) and ending (exclusive) address, if VMA is found. */
+ __u64 vma_start; /* out */
+ __u64 vma_end; /* out */
+ /* VMA permissions flags. A combination of PROCMAP_QUERY_VMA_* flags. */
+ __u64 vma_flags; /* out */
+ /* VMA backing page size granularity. */
+ __u32 vma_page_size; /* out */
+ /*
+ * VMA file offset. If VMA has file backing, this specifies offset
+ * within the file that VMA's start address corresponds to.
+ * Is set to zero if VMA has no backing file.
+ */
+ __u64 vma_offset; /* out */
+ /* Backing file's inode number, or zero, if VMA has no backing file. */
+ __u64 inode; /* out */
+ /* Backing file's device major/minor number, or zero, if VMA has no backing file. */
+ __u32 dev_major; /* out */
+ __u32 dev_minor; /* out */
+ /*
+ * If set to non-zero value, signals the request to return VMA name
+ * (i.e., VMA's backing file's absolute path, with " (deleted)" suffix
+ * appended, if file was unlinked from FS) for matched VMA. VMA name
+ * can also be some special name (e.g., "[heap]", "[stack]") or could
+ * be even user-supplied with prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME).
+ *
+ * Kernel will set this field to zero, if VMA has no associated name.
+ * Otherwise kernel will return actual amount of bytes filled in
+ * user-supplied buffer (see vma_name_addr field below), including the
+ * terminating zero.
+ *
+ * If VMA name is longer that user-supplied maximum buffer size,
+ * -E2BIG error is returned.
+ *
+ * If this field is set to non-zero value, vma_name_addr should point
+ * to valid user space memory buffer of at least vma_name_size bytes.
+ * If set to zero, vma_name_addr should be set to zero as well
+ */
+ __u32 vma_name_size; /* in/out */
+ /*
+ * If set to non-zero value, signals the request to extract and return
+ * VMA's backing file's build ID, if the backing file is an ELF file
+ * and it contains embedded build ID.
+ *
+ * Kernel will set this field to zero, if VMA has no backing file,
+ * backing file is not an ELF file, or ELF file has no build ID
+ * embedded.
+ *
+ * Build ID is a binary value (not a string). Kernel will set
+ * build_id_size field to exact number of bytes used for build ID.
+ * If build ID is requested and present, but needs more bytes than
+ * user-supplied maximum buffer size (see build_id_addr field below),
+ * -E2BIG error will be returned.
+ *
+ * If this field is set to non-zero value, build_id_addr should point
+ * to valid user space memory buffer of at least build_id_size bytes.
+ * If set to zero, build_id_addr should be set to zero as well
+ */
+ __u32 build_id_size; /* in/out */
+ /*
+ * User-supplied address of a buffer of at least vma_name_size bytes
+ * for kernel to fill with matched VMA's name (see vma_name_size field
+ * description above for details).
+ *
+ * Should be set to zero if VMA name should not be returned.
+ */
+ __u64 vma_name_addr; /* in */
+ /*
+ * User-supplied address of a buffer of at least build_id_size bytes
+ * for kernel to fill with matched VMA's ELF build ID, if available
+ * (see build_id_size field description above for details).
+ *
+ * Should be set to zero if build ID should not be returned.
+ */
+ __u64 build_id_addr; /* in */
+};
+
+#endif /* _UAPI_LINUX_FS_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 6/6] selftests/proc: add PROCMAP_QUERY ioctl tests
2024-06-18 22:45 [PATCH v5 0/6] ioctl()-based API to query VMAs from /proc/<pid>/maps Andrii Nakryiko
` (4 preceding siblings ...)
2024-06-18 22:45 ` [PATCH v5 5/6] tools: sync uapi/linux/fs.h header into tools subdir Andrii Nakryiko
@ 2024-06-18 22:45 ` Andrii Nakryiko
5 siblings, 0 replies; 11+ messages in thread
From: Andrii Nakryiko @ 2024-06-18 22:45 UTC (permalink / raw)
To: linux-fsdevel, brauner, viro, akpm
Cc: linux-kernel, bpf, gregkh, linux-mm, liam.howlett, surenb, rppt,
adobriyan, Andrii Nakryiko
Extend existing proc-pid-vm.c tests with PROCMAP_QUERY ioctl() API.
Test a few successful and negative cases, validating querying filtering
and exact vs next VMA logic works as expected.
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
tools/testing/selftests/proc/Makefile | 1 +
tools/testing/selftests/proc/proc-pid-vm.c | 86 ++++++++++++++++++++++
2 files changed, 87 insertions(+)
diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile
index cd95369254c0..291e7087f1b3 100644
--- a/tools/testing/selftests/proc/Makefile
+++ b/tools/testing/selftests/proc/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -Wall -O2 -Wno-unused-function
CFLAGS += -D_GNU_SOURCE
+CFLAGS += $(TOOLS_INCLUDES)
LDFLAGS += -pthread
TEST_GEN_PROGS :=
diff --git a/tools/testing/selftests/proc/proc-pid-vm.c b/tools/testing/selftests/proc/proc-pid-vm.c
index cacbd2a4aec9..d04685771952 100644
--- a/tools/testing/selftests/proc/proc-pid-vm.c
+++ b/tools/testing/selftests/proc/proc-pid-vm.c
@@ -45,6 +45,7 @@
#include <linux/kdev_t.h>
#include <sys/time.h>
#include <sys/resource.h>
+#include <linux/fs.h>
#include "../kselftest.h"
@@ -492,6 +493,91 @@ int main(void)
assert(buf[13] == '\n');
}
+ /* Test PROCMAP_QUERY ioctl() for /proc/$PID/maps */
+ {
+ char path_buf[256], exp_path_buf[256];
+ struct procmap_query q;
+ int fd, err;
+
+ snprintf(path_buf, sizeof(path_buf), "/proc/%u/maps", pid);
+ fd = open(path_buf, O_RDONLY);
+ if (fd == -1)
+ return 1;
+
+ /* CASE 1: exact MATCH at VADDR */
+ memset(&q, 0, sizeof(q));
+ q.size = sizeof(q);
+ q.query_addr = VADDR;
+ q.query_flags = 0;
+ q.vma_name_addr = (__u64)(unsigned long)path_buf;
+ q.vma_name_size = sizeof(path_buf);
+
+ err = ioctl(fd, PROCMAP_QUERY, &q);
+ assert(err == 0);
+
+ assert(q.query_addr == VADDR);
+ assert(q.query_flags == 0);
+
+ assert(q.vma_flags == (PROCMAP_QUERY_VMA_READABLE | PROCMAP_QUERY_VMA_EXECUTABLE));
+ assert(q.vma_start == VADDR);
+ assert(q.vma_end == VADDR + PAGE_SIZE);
+ assert(q.vma_page_size == PAGE_SIZE);
+
+ assert(q.vma_offset == 0);
+ assert(q.inode == st.st_ino);
+ assert(q.dev_major == MAJOR(st.st_dev));
+ assert(q.dev_minor == MINOR(st.st_dev));
+
+ snprintf(exp_path_buf, sizeof(exp_path_buf),
+ "/tmp/#%llu (deleted)", (unsigned long long)st.st_ino);
+ assert(q.vma_name_size == strlen(exp_path_buf) + 1);
+ assert(strcmp(path_buf, exp_path_buf) == 0);
+
+ /* CASE 2: NO MATCH at VADDR-1 */
+ memset(&q, 0, sizeof(q));
+ q.size = sizeof(q);
+ q.query_addr = VADDR - 1;
+ q.query_flags = 0; /* exact match */
+
+ err = ioctl(fd, PROCMAP_QUERY, &q);
+ err = err < 0 ? -errno : 0;
+ assert(err == -ENOENT);
+
+ /* CASE 3: MATCH COVERING_OR_NEXT_VMA at VADDR - 1 */
+ memset(&q, 0, sizeof(q));
+ q.size = sizeof(q);
+ q.query_addr = VADDR - 1;
+ q.query_flags = PROCMAP_QUERY_COVERING_OR_NEXT_VMA;
+
+ err = ioctl(fd, PROCMAP_QUERY, &q);
+ assert(err == 0);
+
+ assert(q.query_addr == VADDR - 1);
+ assert(q.query_flags == PROCMAP_QUERY_COVERING_OR_NEXT_VMA);
+ assert(q.vma_start == VADDR);
+ assert(q.vma_end == VADDR + PAGE_SIZE);
+
+ /* CASE 4: NO MATCH at VADDR + PAGE_SIZE */
+ memset(&q, 0, sizeof(q));
+ q.size = sizeof(q);
+ q.query_addr = VADDR + PAGE_SIZE; /* point right after the VMA */
+ q.query_flags = PROCMAP_QUERY_COVERING_OR_NEXT_VMA;
+
+ err = ioctl(fd, PROCMAP_QUERY, &q);
+ err = err < 0 ? -errno : 0;
+ assert(err == -ENOENT);
+
+ /* CASE 5: NO MATCH WRITABLE at VADDR */
+ memset(&q, 0, sizeof(q));
+ q.size = sizeof(q);
+ q.query_addr = VADDR;
+ q.query_flags = PROCMAP_QUERY_VMA_WRITABLE;
+
+ err = ioctl(fd, PROCMAP_QUERY, &q);
+ err = err < 0 ? -errno : 0;
+ assert(err == -ENOENT);
+ }
+
return 0;
}
#else
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread