Message ID | 20250113055231.5908-1-wegao@suse.com |
---|---|
State | Changes Requested |
Headers | show |
Series | [v1] ioctl10.c: New case test PROCMAP_QUERY ioctl() | expand |
On Mon, Jan 13, 2025 at 12:52:31AM -0500, Wei Gao wrote: > Signed-off-by: Wei Gao <wegao@suse.com> > --- > configure.ac | 1 + > runtest/syscalls | 1 + > testcases/kernel/syscalls/ioctl/.gitignore | 1 + > testcases/kernel/syscalls/ioctl/ioctl10.c | 175 +++++++++++++++++++++ > 4 files changed, 178 insertions(+) > create mode 100644 testcases/kernel/syscalls/ioctl/ioctl10.c The test report error when i check tw in my env(wrong dev_minor number). I guess is kernel issue? susetest:~/ltp # uname -r 6.12.8-2-default susetest:~/ltp # cat /etc/os-release NAME="openSUSE Tumbleweed" # VERSION="20250109" ID="opensuse-tumbleweed" ID_LIKE="opensuse suse" VERSION_ID="20250109" PRETTY_NAME="openSUSE Tumbleweed" ANSI_COLOR="0;32" # CPE 2.3 format, boo#1217921 CPE_NAME="cpe:2.3:o:opensuse:tumbleweed:20250109:*:*:*:*:*:*:*" #CPE 2.2 format #CPE_NAME="cpe:/o:opensuse:tumbleweed:20250109" BUG_REPORT_URL="https://bugzilla.opensuse.org" SUPPORT_URL="https://bugs.opensuse.org" HOME_URL="https://www.opensuse.org" DOCUMENTATION_URL="https://en.opensuse.org/Portal:Tumbleweed" LOGO="distributor-logo-Tumbleweed" tst_test.c:1893: TINFO: LTP version: 20240524-413-g96a255983 tst_test.c:1897: TINFO: Tested kernel: 6.12.8-2-default #1 SMP PREEMPT_DYNAMIC Mon Jan 6 06:45:37 UTC 2025 (90b0f5b) x86_64 tst_test.c:1730: TINFO: Timeout per run is 0h 00m 30s line=00400000-00431000 r-xp 00000000 00:30 14632 /root/ioctl09 ioctl09.c:100: TPASS: parse_maps_file(path_buf, "*", &entry) passed ID of containing device: [0,15] ioctl09.c:120: TPASS: ioctl(fd, PROCMAP_QUERY, &q) passed ioctl09.c:122: TPASS: q.query_addr == entry.vm_start (4194304) ioctl09.c:123: TPASS: q.query_flags == 0 (0) ioctl09.c:124: TPASS: q.vma_flags == entry.vm_flags (5) ioctl09.c:125: TPASS: q.vma_start == entry.vm_start (4194304) ioctl09.c:126: TPASS: q.vma_end == entry.vm_end (4395008) ioctl09.c:127: TPASS: q.vma_page_size == getpagesize() (4096) ioctl09.c:128: TPASS: q.vma_offset == entry.vm_pgoff (0) ioctl09.c:129: TPASS: q.inode == entry.vm_inode (14632) ioctl09.c:130: TPASS: q.dev_major == entry.vm_major (0) ioctl09.c:131: TFAIL: q.dev_minor (35) != entry.vm_minor (48) <<<<<<<<<<<<<<<<<<<<<<<< ioctl09.c:139: TPASS: ioctl(fd, PROCMAP_QUERY, &q) : ENOENT (2) ioctl09.c:147: TPASS: ioctl(fd, PROCMAP_QUERY, &q) passed line=00400000-00431000 r-xp 00000000 00:30 14632 /root/ioctl09 ioctl09.c:151: TPASS: parse_maps_file("/proc/self/maps", "*r-?p *", &entry) passed ioctl09.c:157: TPASS: ioctl(fd, PROCMAP_QUERY, &q) : ENOENT (2) line=00400000-00431000 r-xp 00000000 00:30 14632 /root/ioctl09 ioctl09.c:166: TPASS: parse_maps_file("/proc/self/maps", pattern, &entry) passed ioctl09.c:175: TPASS: ioctl(fd, PROCMAP_QUERY, &q) passed ioctl09.c:176: TPASS: q.vma_name_size == strlen(process_name) + 1 (14) ioctl09.c:177: TPASS: (char *)q.vma_name_addr == process_name (/root/ioctl09) stat /root/ioctl09 File: /root/ioctl09 Size: 873728 Blocks: 1712 IO Block: 4096 regular file Device: 0,48 <<<<<< Inode: 14632 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2025-01-12 22:29:01.378310333 -0500 Modify: 2025-01-12 22:28:58.043307554 -0500 Change: 2025-01-12 22:28:58.043307554 -0500 Birth: 2025-01-12 21:37:52.621841127 -0500
Test with 6.13.0-rc7-1.g9a3f661-vanilla kernel, the result is good. susetest:~/ltp/testcases/kernel/syscalls/ioctl # ./ioctl10 tst_test.c:1900: TINFO: LTP version: 20240930-184-g21afd7d9b tst_test.c:1904: TINFO: Tested kernel: 6.13.0-rc7-1.g9a3f661-vanilla #1 SMP PREEMPT_DYNAMIC Mon Jan 13 11:16:27 UTC 2025 (9a3f661) x86_64 tst_kconfig.c:88: TINFO: Parsing kernel config '/proc/config.gz' tst_kconfig.c:667: TINFO: CONFIG_LATENCYTOP kernel option detected which might slow the execution tst_test.c:1722: TINFO: Overall timeout per run is 0h 02m 00s ioctl10.c:97: TPASS: parse_maps_file(path_buf, "*", &entry) passed ioctl10.c:105: TPASS: ioctl(fd, PROCMAP_QUERY, &q) passed ioctl10.c:107: TPASS: q.query_addr == entry.vm_start (4194304) ioctl10.c:108: TPASS: q.query_flags == 0 (0) ioctl10.c:109: TPASS: q.vma_flags == entry.vm_flags (1) ioctl10.c:110: TPASS: q.vma_start == entry.vm_start (4194304) ioctl10.c:111: TPASS: q.vma_end == entry.vm_end (4210688) ioctl10.c:112: TPASS: q.vma_page_size == getpagesize() (4096) ioctl10.c:113: TPASS: q.vma_offset == entry.vm_pgoff (0) ioctl10.c:114: TPASS: q.inode == entry.vm_inode (11443) ioctl10.c:115: TPASS: q.dev_major == entry.vm_major (0) ioctl10.c:116: TPASS: q.dev_minor == entry.vm_minor (35) ioctl10.c:124: TPASS: ioctl(fd, PROCMAP_QUERY, &q) : ENOENT (2) ioctl10.c:132: TPASS: ioctl(fd, PROCMAP_QUERY, &q) passed ioctl10.c:136: TPASS: parse_maps_file(path_buf, "*r-?p *", &entry) passed ioctl10.c:142: TPASS: ioctl(fd, PROCMAP_QUERY, &q) : ENOENT (2) ioctl10.c:152: TPASS: parse_maps_file(path_buf, pattern, &entry) passed ioctl10.c:161: TPASS: ioctl(fd, PROCMAP_QUERY, &q) passed ioctl10.c:162: TPASS: q.vma_name_size == strlen(process_name) + 1 (50) ioctl10.c:163: TPASS: (char *)q.vma_name_addr == process_name (/root/ltp/testcases/kernel/syscalls/ioctl/ioctl10) Summary: passed 20 failed 0 broken 0 skipped 0 warnings 0 On Mon, Jan 13, 2025 at 1:57 PM Wei Gao <wegao@suse.com> wrote: > On Mon, Jan 13, 2025 at 12:52:31AM -0500, Wei Gao wrote: > > Signed-off-by: Wei Gao <wegao@suse.com> > > --- > > configure.ac | 1 + > > runtest/syscalls | 1 + > > testcases/kernel/syscalls/ioctl/.gitignore | 1 + > > testcases/kernel/syscalls/ioctl/ioctl10.c | 175 +++++++++++++++++++++ > > 4 files changed, 178 insertions(+) > > create mode 100644 testcases/kernel/syscalls/ioctl/ioctl10.c > > The test report error when i check tw in my env(wrong dev_minor number). I > guess is kernel issue? > > susetest:~/ltp # uname -r > 6.12.8-2-default > susetest:~/ltp # cat /etc/os-release > NAME="openSUSE Tumbleweed" > # VERSION="20250109" > ID="opensuse-tumbleweed" > ID_LIKE="opensuse suse" > VERSION_ID="20250109" > PRETTY_NAME="openSUSE Tumbleweed" > ANSI_COLOR="0;32" > # CPE 2.3 format, boo#1217921 > CPE_NAME="cpe:2.3:o:opensuse:tumbleweed:20250109:*:*:*:*:*:*:*" > #CPE 2.2 format > #CPE_NAME="cpe:/o:opensuse:tumbleweed:20250109" > BUG_REPORT_URL="https://bugzilla.opensuse.org" > SUPPORT_URL="https://bugs.opensuse.org" > HOME_URL="https://www.opensuse.org" > DOCUMENTATION_URL="https://en.opensuse.org/Portal:Tumbleweed" > LOGO="distributor-logo-Tumbleweed" > > tst_test.c:1893: TINFO: LTP version: 20240524-413-g96a255983 > tst_test.c:1897: TINFO: Tested kernel: 6.12.8-2-default #1 SMP > PREEMPT_DYNAMIC Mon Jan 6 06:45:37 UTC 2025 (90b0f5b) x86_64 > tst_test.c:1730: TINFO: Timeout per run is 0h 00m 30s > line=00400000-00431000 r-xp 00000000 00:30 14632 > /root/ioctl09 > > ioctl09.c:100: TPASS: parse_maps_file(path_buf, "*", &entry) passed > ID of containing device: [0,15] > ioctl09.c:120: TPASS: ioctl(fd, PROCMAP_QUERY, &q) passed > ioctl09.c:122: TPASS: q.query_addr == entry.vm_start (4194304) > ioctl09.c:123: TPASS: q.query_flags == 0 (0) > ioctl09.c:124: TPASS: q.vma_flags == entry.vm_flags (5) > ioctl09.c:125: TPASS: q.vma_start == entry.vm_start (4194304) > ioctl09.c:126: TPASS: q.vma_end == entry.vm_end (4395008) > ioctl09.c:127: TPASS: q.vma_page_size == getpagesize() (4096) > ioctl09.c:128: TPASS: q.vma_offset == entry.vm_pgoff (0) > ioctl09.c:129: TPASS: q.inode == entry.vm_inode (14632) > ioctl09.c:130: TPASS: q.dev_major == entry.vm_major (0) > ioctl09.c:131: TFAIL: q.dev_minor (35) != entry.vm_minor (48) > <<<<<<<<<<<<<<<<<<<<<<<< > ioctl09.c:139: TPASS: ioctl(fd, PROCMAP_QUERY, &q) : ENOENT (2) > ioctl09.c:147: TPASS: ioctl(fd, PROCMAP_QUERY, &q) passed > line=00400000-00431000 r-xp 00000000 00:30 14632 > /root/ioctl09 > > ioctl09.c:151: TPASS: parse_maps_file("/proc/self/maps", "*r-?p *", > &entry) passed > ioctl09.c:157: TPASS: ioctl(fd, PROCMAP_QUERY, &q) : ENOENT (2) > line=00400000-00431000 r-xp 00000000 00:30 14632 > /root/ioctl09 > > ioctl09.c:166: TPASS: parse_maps_file("/proc/self/maps", pattern, &entry) > passed > ioctl09.c:175: TPASS: ioctl(fd, PROCMAP_QUERY, &q) passed > ioctl09.c:176: TPASS: q.vma_name_size == strlen(process_name) + 1 (14) > ioctl09.c:177: TPASS: (char *)q.vma_name_addr == process_name > (/root/ioctl09) > > stat /root/ioctl09 > File: /root/ioctl09 > Size: 873728 Blocks: 1712 IO Block: 4096 regular file > Device: 0,48 <<<<<< Inode: 14632 Links: 1 > Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) > Access: 2025-01-12 22:29:01.378310333 -0500 > Modify: 2025-01-12 22:28:58.043307554 -0500 > Change: 2025-01-12 22:28:58.043307554 -0500 > Birth: 2025-01-12 21:37:52.621841127 -0500 > >
Hi! > diff --git a/configure.ac b/configure.ac > index 6992d75ca..56380d41e 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -179,6 +179,7 @@ AC_CHECK_TYPES([struct fanotify_event_info_fid, struct fanotify_event_info_error > struct fanotify_event_info_header, struct fanotify_event_info_pidfd],,,[#include <sys/fanotify.h>]) > AC_CHECK_TYPES([struct file_clone_range],,,[#include <linux/fs.h>]) > AC_CHECK_TYPES([struct file_dedupe_range],,,[#include <linux/fs.h>]) > +AC_CHECK_TYPES([struct procmap_query],,,[#include <linux/fs.h>]) > > AC_CHECK_TYPES([struct file_handle],,,[ > #define _GNU_SOURCE > diff --git a/runtest/syscalls b/runtest/syscalls > index ded035ee8..a13811855 100644 > --- a/runtest/syscalls > +++ b/runtest/syscalls > @@ -583,6 +583,7 @@ ioctl06 ioctl06 > ioctl07 ioctl07 > ioctl08 ioctl08 > ioctl09 ioctl09 > +ioctl10 ioctl10 > > ioctl_loop01 ioctl_loop01 > ioctl_loop02 ioctl_loop02 > diff --git a/testcases/kernel/syscalls/ioctl/.gitignore b/testcases/kernel/syscalls/ioctl/.gitignore > index 1f099ff95..9c3f66bf1 100644 > --- a/testcases/kernel/syscalls/ioctl/.gitignore > +++ b/testcases/kernel/syscalls/ioctl/.gitignore > @@ -7,6 +7,7 @@ > /ioctl07 > /ioctl08 > /ioctl09 > +/ioctl10 > /ioctl_loop01 > /ioctl_loop02 > /ioctl_loop03 > diff --git a/testcases/kernel/syscalls/ioctl/ioctl10.c b/testcases/kernel/syscalls/ioctl/ioctl10.c > new file mode 100644 > index 000000000..cd9e3c528 > --- /dev/null > +++ b/testcases/kernel/syscalls/ioctl/ioctl10.c > @@ -0,0 +1,175 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright (c) 2024 Wei Gao <wegao@suse.com> > + */ > + > +/*\ > + * [Description] > + * > + * Test PROCMAP_QUERY ioctl() for /proc/$PID/maps. > + * Test base on kernel selftests proc-pid-vm.c. > + * > + * 1. Ioctl with exact match query_addr > + * 2. Ioctl without match query_addr > + * 3. Check COVERING_OR_NEXT_VMA query_flags > + * 4. Check PROCMAP_QUERY_VMA_WRITABLE query_flags > + * 5. Check vma_name_addr content > + */ > + > +#include "config.h" > +#include <stdlib.h> > +#include <sys/ioctl.h> > +#include <errno.h> > +#include <fnmatch.h> > +#include "tst_test.h" > +#include "tst_safe_stdio.h" > +#include <sys/sysmacros.h> > + > +#ifdef HAVE_STRUCT_PROCMAP_QUERY Instead of this we should add a lapi fallback, as we do for other newly introduced functionality. See include/lapi/ficlone.h how that should look like. > +#include <linux/fs.h> > + > +struct map_entry { > + unsigned long vm_start; > + unsigned long vm_end; > + char vm_flags_str[5]; > + unsigned long vm_pgoff; > + unsigned int vm_major; > + unsigned int vm_minor; > + unsigned long vm_inode; > + char vm_name[256]; > + unsigned int vm_flags; > +}; > + > +static unsigned int parse_vm_flags(const char *vm_flags_str) > +{ > + unsigned int flags = 0; > + > + if (strchr(vm_flags_str, 'r')) > + flags |= PROCMAP_QUERY_VMA_READABLE; > + if (strchr(vm_flags_str, 'w')) > + flags |= PROCMAP_QUERY_VMA_WRITABLE; > + if (strchr(vm_flags_str, 'x')) > + flags |= PROCMAP_QUERY_VMA_EXECUTABLE; > + if (strchr(vm_flags_str, 's')) > + flags |= PROCMAP_QUERY_VMA_SHARED; > + > + return flags; > + > +} > + > +static int parse_maps_file(const char *filename, const char *keyword, struct map_entry *entry) > +{ > + FILE *fp = SAFE_FOPEN(filename, "r"); > + > + char line[1024]; > + > + while (fgets(line, sizeof(line), fp) != NULL) { > + if (fnmatch(keyword, line, 0) == 0) { > + if (sscanf(line, "%lx-%lx %s %lx %x:%x %lu %s", > + &entry->vm_start, &entry->vm_end, entry->vm_flags_str, > + &entry->vm_pgoff, &entry->vm_major, &entry->vm_minor, > + &entry->vm_inode, entry->vm_name) < 7) > + return -1; > + > + entry->vm_flags = parse_vm_flags(entry->vm_flags_str); > + > + SAFE_FCLOSE(fp); > + return 0; > + } > + } > + > + SAFE_FCLOSE(fp); > + return -1; > +} > + > +static void verify_ioctl(void) > +{ > + char path_buf[256]; > + struct procmap_query q; > + int fd; > + struct map_entry entry; > + > + memset(&entry, 0, sizeof(entry)); > + > + snprintf(path_buf, sizeof(path_buf), "/proc/%u/maps", getpid()); > + fd = SAFE_OPEN(path_buf, O_RDONLY); You don't have to create the path with $PID you can pass "/proc/self/maps" to the open() instead. > + TST_EXP_PASS(parse_maps_file(path_buf, "*", &entry)); This isn't a test, so we should call it in TST_EXP_PASS() instead the function should call tst_brk() on a failure and shouldn't return a value. > + /* CASE 1: exact MATCH at query_addr */ > + memset(&q, 0, sizeof(q)); > + q.size = sizeof(q); > + q.query_addr = (__u64)entry.vm_start; ^ Should be uint64_t in userspace. > + q.query_flags = 0; > + > + TST_EXP_PASS(ioctl(fd, PROCMAP_QUERY, &q)); > + > + TST_EXP_EQ_LU(q.query_addr, entry.vm_start); > + TST_EXP_EQ_LU(q.query_flags, 0); > + TST_EXP_EQ_LU(q.vma_flags, entry.vm_flags); > + TST_EXP_EQ_LU(q.vma_start, entry.vm_start); > + TST_EXP_EQ_LU(q.vma_end, entry.vm_end); > + TST_EXP_EQ_LU(q.vma_page_size, getpagesize()); > + TST_EXP_EQ_LU(q.vma_offset, entry.vm_pgoff); > + TST_EXP_EQ_LU(q.inode, entry.vm_inode); > + TST_EXP_EQ_LU(q.dev_major, entry.vm_major); > + TST_EXP_EQ_LU(q.dev_minor, entry.vm_minor); > + > + /* CASE 2: NO MATCH at query_addr */ > + memset(&q, 0, sizeof(q)); > + q.size = sizeof(q); > + q.query_addr = entry.vm_start - 1; > + q.query_flags = 0; > + > + TST_EXP_FAIL(ioctl(fd, PROCMAP_QUERY, &q), ENOENT); > + > + /* CASE 3: MATCH COVERING_OR_NEXT_VMA */ > + memset(&q, 0, sizeof(q)); > + q.size = sizeof(q); > + q.query_addr = entry.vm_start - 1; > + q.query_flags = PROCMAP_QUERY_COVERING_OR_NEXT_VMA; > + > + TST_EXP_PASS(ioctl(fd, PROCMAP_QUERY, &q)); > + > + /* CASE 4: NO MATCH WRITABLE at query_addr */ > + memset(&entry, 0, sizeof(entry)); > + TST_EXP_PASS(parse_maps_file(path_buf, "*r-?p *", &entry)); Here as well. > + memset(&q, 0, sizeof(q)); > + q.size = sizeof(q); > + q.query_addr = entry.vm_start; > + q.query_flags = PROCMAP_QUERY_VMA_WRITABLE; > + TST_EXP_FAIL(ioctl(fd, PROCMAP_QUERY, &q), ENOENT); > + > + /* CASE 5: check vma_name_addr content */ > + char process_name[256]; > + char pattern[256]; > + char buf[256]; > + > + SAFE_READLINK("/proc/self/exe", process_name, sizeof(process_name)); > + sprintf(pattern, "*%s*", process_name); > + memset(&entry, 0, sizeof(entry)); > + TST_EXP_PASS(parse_maps_file(path_buf, pattern, &entry)); Here as well. > + memset(&q, 0, sizeof(q)); > + q.size = sizeof(q); > + q.query_addr = entry.vm_start; > + q.query_flags = 0; > + q.vma_name_addr = (__u64)(unsigned long)buf; Here as well. > + q.vma_name_size = sizeof(buf); > + > + TST_EXP_PASS(ioctl(fd, PROCMAP_QUERY, &q)); > + TST_EXP_EQ_LU(q.vma_name_size, strlen(process_name) + 1); > + TST_EXP_EQ_STR((char *)q.vma_name_addr, process_name); > + > + SAFE_CLOSE(fd); > +} > + > +static struct tst_test test = { > + .test_all = verify_ioctl, > + .needs_root = 1, > +}; > +#else > + TST_TEST_TCONF( > + "This system does not provide support for ioctl(PROCMAP_QUERY)"); > +#endif
diff --git a/configure.ac b/configure.ac index 6992d75ca..56380d41e 100644 --- a/configure.ac +++ b/configure.ac @@ -179,6 +179,7 @@ AC_CHECK_TYPES([struct fanotify_event_info_fid, struct fanotify_event_info_error struct fanotify_event_info_header, struct fanotify_event_info_pidfd],,,[#include <sys/fanotify.h>]) AC_CHECK_TYPES([struct file_clone_range],,,[#include <linux/fs.h>]) AC_CHECK_TYPES([struct file_dedupe_range],,,[#include <linux/fs.h>]) +AC_CHECK_TYPES([struct procmap_query],,,[#include <linux/fs.h>]) AC_CHECK_TYPES([struct file_handle],,,[ #define _GNU_SOURCE diff --git a/runtest/syscalls b/runtest/syscalls index ded035ee8..a13811855 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -583,6 +583,7 @@ ioctl06 ioctl06 ioctl07 ioctl07 ioctl08 ioctl08 ioctl09 ioctl09 +ioctl10 ioctl10 ioctl_loop01 ioctl_loop01 ioctl_loop02 ioctl_loop02 diff --git a/testcases/kernel/syscalls/ioctl/.gitignore b/testcases/kernel/syscalls/ioctl/.gitignore index 1f099ff95..9c3f66bf1 100644 --- a/testcases/kernel/syscalls/ioctl/.gitignore +++ b/testcases/kernel/syscalls/ioctl/.gitignore @@ -7,6 +7,7 @@ /ioctl07 /ioctl08 /ioctl09 +/ioctl10 /ioctl_loop01 /ioctl_loop02 /ioctl_loop03 diff --git a/testcases/kernel/syscalls/ioctl/ioctl10.c b/testcases/kernel/syscalls/ioctl/ioctl10.c new file mode 100644 index 000000000..cd9e3c528 --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl10.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Wei Gao <wegao@suse.com> + */ + +/*\ + * [Description] + * + * Test PROCMAP_QUERY ioctl() for /proc/$PID/maps. + * Test base on kernel selftests proc-pid-vm.c. + * + * 1. Ioctl with exact match query_addr + * 2. Ioctl without match query_addr + * 3. Check COVERING_OR_NEXT_VMA query_flags + * 4. Check PROCMAP_QUERY_VMA_WRITABLE query_flags + * 5. Check vma_name_addr content + */ + +#include "config.h" +#include <stdlib.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fnmatch.h> +#include "tst_test.h" +#include "tst_safe_stdio.h" +#include <sys/sysmacros.h> + +#ifdef HAVE_STRUCT_PROCMAP_QUERY +#include <linux/fs.h> + +struct map_entry { + unsigned long vm_start; + unsigned long vm_end; + char vm_flags_str[5]; + unsigned long vm_pgoff; + unsigned int vm_major; + unsigned int vm_minor; + unsigned long vm_inode; + char vm_name[256]; + unsigned int vm_flags; +}; + +static unsigned int parse_vm_flags(const char *vm_flags_str) +{ + unsigned int flags = 0; + + if (strchr(vm_flags_str, 'r')) + flags |= PROCMAP_QUERY_VMA_READABLE; + if (strchr(vm_flags_str, 'w')) + flags |= PROCMAP_QUERY_VMA_WRITABLE; + if (strchr(vm_flags_str, 'x')) + flags |= PROCMAP_QUERY_VMA_EXECUTABLE; + if (strchr(vm_flags_str, 's')) + flags |= PROCMAP_QUERY_VMA_SHARED; + + return flags; + +} + +static int parse_maps_file(const char *filename, const char *keyword, struct map_entry *entry) +{ + FILE *fp = SAFE_FOPEN(filename, "r"); + + char line[1024]; + + while (fgets(line, sizeof(line), fp) != NULL) { + if (fnmatch(keyword, line, 0) == 0) { + if (sscanf(line, "%lx-%lx %s %lx %x:%x %lu %s", + &entry->vm_start, &entry->vm_end, entry->vm_flags_str, + &entry->vm_pgoff, &entry->vm_major, &entry->vm_minor, + &entry->vm_inode, entry->vm_name) < 7) + return -1; + + entry->vm_flags = parse_vm_flags(entry->vm_flags_str); + + SAFE_FCLOSE(fp); + return 0; + } + } + + SAFE_FCLOSE(fp); + return -1; +} + +static void verify_ioctl(void) +{ + char path_buf[256]; + struct procmap_query q; + int fd; + struct map_entry entry; + + memset(&entry, 0, sizeof(entry)); + + snprintf(path_buf, sizeof(path_buf), "/proc/%u/maps", getpid()); + fd = SAFE_OPEN(path_buf, O_RDONLY); + + TST_EXP_PASS(parse_maps_file(path_buf, "*", &entry)); + + /* CASE 1: exact MATCH at query_addr */ + memset(&q, 0, sizeof(q)); + q.size = sizeof(q); + q.query_addr = (__u64)entry.vm_start; + q.query_flags = 0; + + TST_EXP_PASS(ioctl(fd, PROCMAP_QUERY, &q)); + + TST_EXP_EQ_LU(q.query_addr, entry.vm_start); + TST_EXP_EQ_LU(q.query_flags, 0); + TST_EXP_EQ_LU(q.vma_flags, entry.vm_flags); + TST_EXP_EQ_LU(q.vma_start, entry.vm_start); + TST_EXP_EQ_LU(q.vma_end, entry.vm_end); + TST_EXP_EQ_LU(q.vma_page_size, getpagesize()); + TST_EXP_EQ_LU(q.vma_offset, entry.vm_pgoff); + TST_EXP_EQ_LU(q.inode, entry.vm_inode); + TST_EXP_EQ_LU(q.dev_major, entry.vm_major); + TST_EXP_EQ_LU(q.dev_minor, entry.vm_minor); + + /* CASE 2: NO MATCH at query_addr */ + memset(&q, 0, sizeof(q)); + q.size = sizeof(q); + q.query_addr = entry.vm_start - 1; + q.query_flags = 0; + + TST_EXP_FAIL(ioctl(fd, PROCMAP_QUERY, &q), ENOENT); + + /* CASE 3: MATCH COVERING_OR_NEXT_VMA */ + memset(&q, 0, sizeof(q)); + q.size = sizeof(q); + q.query_addr = entry.vm_start - 1; + q.query_flags = PROCMAP_QUERY_COVERING_OR_NEXT_VMA; + + TST_EXP_PASS(ioctl(fd, PROCMAP_QUERY, &q)); + + /* CASE 4: NO MATCH WRITABLE at query_addr */ + memset(&entry, 0, sizeof(entry)); + TST_EXP_PASS(parse_maps_file(path_buf, "*r-?p *", &entry)); + + memset(&q, 0, sizeof(q)); + q.size = sizeof(q); + q.query_addr = entry.vm_start; + q.query_flags = PROCMAP_QUERY_VMA_WRITABLE; + TST_EXP_FAIL(ioctl(fd, PROCMAP_QUERY, &q), ENOENT); + + /* CASE 5: check vma_name_addr content */ + char process_name[256]; + char pattern[256]; + char buf[256]; + + SAFE_READLINK("/proc/self/exe", process_name, sizeof(process_name)); + sprintf(pattern, "*%s*", process_name); + memset(&entry, 0, sizeof(entry)); + TST_EXP_PASS(parse_maps_file(path_buf, pattern, &entry)); + + memset(&q, 0, sizeof(q)); + q.size = sizeof(q); + q.query_addr = entry.vm_start; + q.query_flags = 0; + q.vma_name_addr = (__u64)(unsigned long)buf; + q.vma_name_size = sizeof(buf); + + TST_EXP_PASS(ioctl(fd, PROCMAP_QUERY, &q)); + TST_EXP_EQ_LU(q.vma_name_size, strlen(process_name) + 1); + TST_EXP_EQ_STR((char *)q.vma_name_addr, process_name); + + SAFE_CLOSE(fd); +} + +static struct tst_test test = { + .test_all = verify_ioctl, + .needs_root = 1, +}; +#else + TST_TEST_TCONF( + "This system does not provide support for ioctl(PROCMAP_QUERY)"); +#endif
Signed-off-by: Wei Gao <wegao@suse.com> --- configure.ac | 1 + runtest/syscalls | 1 + testcases/kernel/syscalls/ioctl/.gitignore | 1 + testcases/kernel/syscalls/ioctl/ioctl10.c | 175 +++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 testcases/kernel/syscalls/ioctl/ioctl10.c