diff mbox series

[v1] ioctl10.c: New case test PROCMAP_QUERY ioctl()

Message ID 20250113055231.5908-1-wegao@suse.com
State Changes Requested
Headers show
Series [v1] ioctl10.c: New case test PROCMAP_QUERY ioctl() | expand

Commit Message

Wei Gao Jan. 13, 2025, 5:52 a.m. UTC
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

Comments

Wei Gao Jan. 13, 2025, 5:57 a.m. UTC | #1
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
Wei Gao Jan. 14, 2025, 12:08 p.m. UTC | #2
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
>
>
Cyril Hrubis Feb. 21, 2025, 3:04 p.m. UTC | #3
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 mbox series

Patch

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