[LTP] [PATCH 13/13] metadata: Add runtest file generator
Cyril Hrubis
chrubis@suse.cz
Wed Jun 24 11:19:00 CEST 2026
Now just for the runtest/cve file.
The changes in the file are:
- request_key03 is executed once and checks for both CVEs
(we have many tests that check for more than one CVE and we execute
rest of them once as well)
- if we have more than one test per CVE the suffixes are now numbered
(it was random before)
- if test checks for more than one CVE the test tag is the oldest one
(the crypto_user01 test jumps up in the runtest file, but again this
makes it more consistent)
- kvm CVE tests are included, I do not see a reason to filter them out
but we can add that filter if deemed necessary
- the file also includes landlock07 that should have been included but
wasn't
Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
metadata/.gitignore | 1 +
metadata/Makefile | 10 +-
metadata/genruntest.c | 292 ++++++++++++++++++++++++++++++++++++++++++
runtest/.gitignore | 1 +
runtest/cve | 100 ---------------
5 files changed, 302 insertions(+), 102 deletions(-)
create mode 100644 metadata/genruntest.c
create mode 100644 runtest/.gitignore
delete mode 100644 runtest/cve
diff --git a/metadata/.gitignore b/metadata/.gitignore
index bb6399e5c..ae33412a6 100644
--- a/metadata/.gitignore
+++ b/metadata/.gitignore
@@ -1,3 +1,4 @@
metaparse
metaparse-sh
+genruntest
ltp.json
diff --git a/metadata/Makefile b/metadata/Makefile
index 6939b9f76..fcd63f385 100644
--- a/metadata/Makefile
+++ b/metadata/Makefile
@@ -6,15 +6,21 @@ top_srcdir ?= ..
include $(top_srcdir)/include/mk/env_pre.mk
include $(top_srcdir)/include/mk/functions.mk
-MAKE_TARGETS := ltp.json
-HOST_MAKE_TARGETS := metaparse metaparse-sh
+MAKE_TARGETS := ltp.json ../runtest/cve
+HOST_MAKE_TARGETS := metaparse metaparse-sh genruntest
INSTALL_DIR = metadata
+genruntest: genruntest.c $(addprefix $(top_srcdir)/libs/ujson/, ujson_reader.c ujson_common.c ujson_utf.c)
+genruntest: HOST_CFLAGS+=-I$(top_srcdir)/include/
+
.PHONY: ltp.json
ltp.json: metaparse metaparse-sh
$(abs_srcdir)/parse.sh > ltp.json
+../runtest/cve: ltp.json genruntest
+ ${top_builddir}/metadata/genruntest ltp.json cve > ../runtest/cve
+
test:
$(MAKE) -C $(abs_srcdir)/tests/ test
diff --git a/metadata/genruntest.c b/metadata/genruntest.c
new file mode 100644
index 000000000..193c7fa50
--- /dev/null
+++ b/metadata/genruntest.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Cyril Hrubis <chrubis@suse.cz>
+ *
+ * Generate LTP runtest files from ltp.json metadata.
+ *
+ * Usage: genruntest ltp.json cve
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ujson.h>
+
+struct cve_entry {
+ char name[128];
+ char binary[128];
+ int cve_year;
+ int cve_seq;
+ int may_kmemleak;
+};
+
+static struct cve_entry *entries;
+static unsigned int entry_cnt;
+static unsigned int entry_size;
+
+static void add_entry(const char *cve_id, const char *binary,
+ int may_kmemleak)
+{
+ const char *id = cve_id;
+
+ if (entry_cnt >= entry_size) {
+ entry_size = entry_size ? entry_size * 2 : 128;
+ entries = realloc(entries, entry_size * sizeof(*entries));
+ if (!entries) {
+ fprintf(stderr, "Out of memory\n");
+ exit(1);
+ }
+ }
+
+ struct cve_entry *e = &entries[entry_cnt++];
+
+ snprintf(e->name, sizeof(e->name), "cve-%s", id);
+ snprintf(e->binary, sizeof(e->binary), "%s", binary);
+ e->may_kmemleak = may_kmemleak;
+
+ if (sscanf(id, "%d-%d", &e->cve_year, &e->cve_seq) != 2) {
+ fprintf(stderr, "ERROR: Invalid CVE id '%s' should be 'year-seq'\n", cve_id);
+ exit(1);
+ }
+}
+
+static int cmp_entries(const void *a, const void *b)
+{
+ const struct cve_entry *ea = a;
+ const struct cve_entry *eb = b;
+
+ if (ea->cve_year != eb->cve_year)
+ return ea->cve_year - eb->cve_year;
+
+ if (ea->cve_seq != eb->cve_seq)
+ return ea->cve_seq - eb->cve_seq;
+
+ return strcmp(ea->binary, eb->binary);
+}
+
+/*
+ * Count how many entries share the same CVE name.
+ */
+static int count_same(unsigned int idx)
+{
+ int cnt = 1;
+
+ while (idx + cnt < entry_cnt &&
+ !strcmp(entries[idx].name, entries[idx + cnt].name))
+ cnt++;
+
+ return cnt;
+}
+
+static void print_entries(int kmemleak)
+{
+ unsigned int i;
+
+ for (i = 0; i < entry_cnt;) {
+ int cnt = count_same(i);
+ int j;
+
+ for (j = 0; j < cnt; j++) {
+ struct cve_entry *e = &entries[i + j];
+
+ if (e->may_kmemleak != kmemleak)
+ continue;
+
+ if (cnt > 1)
+ printf("%s-%02d %s\n", e->name, j + 1, e->binary);
+ else
+ printf("%s %s\n", e->name, e->binary);
+ }
+
+ i += cnt;
+ }
+}
+
+enum test_attr_ids {
+ ATTR_MAY_KMEMLEAK,
+ ATTR_TAGS,
+};
+
+static ujson_obj_attr test_attrs[] = {
+ UJSON_OBJ_ATTR_IDX(ATTR_MAY_KMEMLEAK, "may_kmemleak", UJSON_BOOL),
+ UJSON_OBJ_ATTR_IDX(ATTR_TAGS, "tags", UJSON_ARR),
+};
+
+static ujson_obj test_obj = {
+ .attrs = test_attrs,
+ .attr_cnt = UJSON_ARRAY_SIZE(test_attrs),
+};
+
+static int parse_cve_tags(ujson_reader *reader, const char *binary,
+ int may_kmemleak, int do_add)
+{
+ char buf[256];
+ ujson_val val = UJSON_VAL_INIT(buf, sizeof(buf));
+ int cnt = 0;
+ char *oldest_id = NULL;
+ int oldest_year = 0, oldest_seq = 0;
+
+ UJSON_ARR_FOREACH(reader, &val) {
+ char *name = NULL;
+ char *value = NULL;
+
+ if (val.type != UJSON_ARR) {
+ ujson_arr_skip(reader);
+ continue;
+ }
+
+ UJSON_ARR_FOREACH(reader, &val) {
+ if (val.type != UJSON_STR)
+ continue;
+
+ if (!name)
+ name = strdup(val.val_str);
+ else if (!value)
+ value = strdup(val.val_str);
+ }
+
+ if (name && value && !strcmp(name, "CVE")) {
+ if (do_add) {
+ const char *id = value;
+ int year, seq;
+
+ if (!strncmp(id, "CVE-", 4))
+ id += 4;
+
+ if (sscanf(id, "%d-%d", &year, &seq) == 2) {
+ if (!oldest_id ||
+ year < oldest_year ||
+ (year == oldest_year && seq < oldest_seq)) {
+ free(oldest_id);
+ oldest_id = strdup(value);
+ oldest_year = year;
+ oldest_seq = seq;
+ }
+ }
+ }
+ cnt++;
+ }
+
+ free(name);
+ free(value);
+ }
+
+ if (do_add && oldest_id)
+ add_entry(oldest_id, binary, may_kmemleak);
+
+ free(oldest_id);
+
+ return cnt;
+}
+
+static void parse_test(ujson_reader *reader, const char *binary)
+{
+ char buf[256];
+ ujson_val val = UJSON_VAL_INIT(buf, sizeof(buf));
+ int may_kmemleak = 0;
+ int cve_cnt = 0;
+
+ ujson_reader_state obj_state = ujson_reader_state_save(reader);
+
+ UJSON_OBJ_FOREACH_FILTER(reader, &val, &test_obj, NULL) {
+ switch ((enum test_attr_ids)val.idx) {
+ case ATTR_MAY_KMEMLEAK:
+ may_kmemleak = val.val_bool;
+ break;
+ case ATTR_TAGS:
+ cve_cnt = parse_cve_tags(reader, NULL, 0, 0);
+ break;
+ }
+ }
+
+ if (!cve_cnt)
+ return;
+
+ ujson_reader_state_load(reader, obj_state);
+
+ UJSON_OBJ_FOREACH_FILTER(reader, &val, &test_obj, NULL) {
+ switch ((enum test_attr_ids)val.idx) {
+ case ATTR_MAY_KMEMLEAK:
+ break;
+ case ATTR_TAGS:
+ parse_cve_tags(reader, binary, may_kmemleak, 1);
+ break;
+ }
+ }
+}
+
+enum top_attr_ids {
+ TOP_TESTS,
+};
+
+static ujson_obj_attr top_attrs[] = {
+ UJSON_OBJ_ATTR_IDX(TOP_TESTS, "tests", UJSON_OBJ),
+};
+
+static ujson_obj top_obj = {
+ .attrs = top_attrs,
+ .attr_cnt = UJSON_ARRAY_SIZE(top_attrs),
+};
+
+static void gen_cve(ujson_reader *reader)
+{
+ char buf[256];
+ ujson_val val = UJSON_VAL_INIT(buf, sizeof(buf));
+
+ UJSON_OBJ_FOREACH_FILTER(reader, &val, &top_obj, NULL) {
+ if (val.idx != TOP_TESTS)
+ continue;
+
+ UJSON_OBJ_FOREACH(reader, &val) {
+ char binary[128];
+
+ snprintf(binary, sizeof(binary), "%s", val.id);
+
+ if (val.type == UJSON_OBJ)
+ parse_test(reader, binary);
+ else
+ ujson_obj_skip(reader);
+ }
+ }
+
+ if (ujson_reader_err(reader)) {
+ ujson_err_print(reader);
+ exit(1);
+ }
+
+ qsort(entries, entry_cnt, sizeof(*entries), cmp_entries);
+
+ printf("# Tests which check for vulnerabilities by CVE number\n");
+ print_entries(0);
+ printf("# Tests below may cause kernel memory leak\n");
+ print_entries(1);
+}
+
+int main(int argc, char *argv[])
+{
+ ujson_reader *reader;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s ltp.json runtest_name\n", argv[0]);
+ return 1;
+ }
+
+ if (strcmp(argv[2], "cve")) {
+ fprintf(stderr, "Unknown runtest: %s\n", argv[2]);
+ return 1;
+ }
+
+ reader = ujson_reader_load(argv[1]);
+ if (!reader) {
+ fprintf(stderr, "Failed to load '%s'\n", argv[1]);
+ return 1;
+ }
+
+ gen_cve(reader);
+
+ ujson_reader_free(reader);
+ free(entries);
+
+ return 0;
+}
diff --git a/runtest/.gitignore b/runtest/.gitignore
new file mode 100644
index 000000000..290696707
--- /dev/null
+++ b/runtest/.gitignore
@@ -0,0 +1 @@
+cve
diff --git a/runtest/cve b/runtest/cve
deleted file mode 100644
index 5f29d1686..000000000
--- a/runtest/cve
+++ /dev/null
@@ -1,100 +0,0 @@
-# Tests which check for vulnerabilities by CVE number
-cve-2011-0999 thp01
-cve-2011-2183 ksm05
-cve-2011-2496 vma03
-cve-2012-0957 uname04
-cve-2014-0196 cve-2014-0196
-cve-2015-0235 gethostbyname_r01
-cve-2015-3290 cve-2015-3290
-cve-2015-7550 keyctl02
-cve-2016-4470 keyctl01.sh
-cve-2016-4997 setsockopt03
-cve-2016-5195 dirtyc0w
-cve-2016-7042 cve-2016-7042
-cve-2016-7117 cve-2016-7117
-cve-2016-8655 setsockopt06
-cve-2016-9604 keyctl08
-cve-2016-9793 setsockopt04
-cve-2016-10044 cve-2016-10044
-cve-2017-2618 cve-2017-2618
-cve-2017-2636 pty05
-cve-2017-2671 cve-2017-2671
-cve-2017-5754 meltdown
-cve-2017-6951 request_key05
-cve-2017-7308 setsockopt02
-cve-2017-7472 keyctl04
-cve-2017-7616 set_mempolicy05
-cve-2017-8890 accept02
-cve-2017-10661 timerfd_settime02
-cve-2017-12192 keyctl07
-cve-2017-12193 add_key04
-cve-2017-15274 add_key02
-cve-2017-15299 request_key03 -b cve-2017-15299
-cve-2017-15537 ptrace07
-cve-2017-15649 fanout01
-cve-2017-15951 request_key03 -b cve-2017-15951
-cve-2017-16939 cve-2017-16939
-cve-2017-16995 bpf_prog03
-cve-2017-17052 cve-2017-17052
-cve-2017-17053 cve-2017-17053
-cve-2017-17712 sendmsg03
-cve-2017-17805 af_alg02
-cve-2017-17806 af_alg01
-cve-2017-17807 request_key04
-cve-2017-18075 pcrypt_aead01
-cve-2017-18344 timer_create03
-cve-2017-1000111 setsockopt07
-cve-2017-1000112 setsockopt05
-cve-2017-1000364 stack_clash
-cve-2017-1000380 snd_timer01
-cve-2017-1000405 thp04
-cve-2018-5803 sctp_big_chunk
-cve-2018-6927 futex_cmp_requeue02
-cve-2018-7566 snd_seq01
-cve-2018-8897 ptrace09
-cve-2018-9568 connect02
-cve-2018-10124 kill13
-cve-2018-11508 adjtimex03
-cve-2018-12896 timer_settime03
-cve-2018-13405 creat09
-cve-2018-18445 bpf_prog04
-cve-2018-18559 bind06
-cve-2018-18955 userns08
-cve-2018-19854 crypto_user01
-cve-2018-1000001 realpath01
-cve-2018-1000199 ptrace08
-cve-2018-1000204 ioctl_sg01
-cve-2019-8912 af_alg07
-cve-2020-11494 pty04
-cve-2020-14386 sendto03
-cve-2020-14416 pty03
-cve-2020-25705 icmp_rate_limit01
-cve-2020-29373 io_uring02
-cve-2020-36557 pty06
-cve-2021-3444 bpf_prog05
-cve-2021-3609 can_bcm01
-cve-2021-4034 execve06
-cve-2021-4197_1 cgroup_core01
-cve-2021-4197_2 cgroup_core02
-cve-2021-4204 bpf_prog06
-cve-2021-22555 setsockopt08
-cve-2021-26708 vsock01
-cve-2021-22600 setsockopt09
-cve-2021-38604 mq_notify03
-cve-2022-0847 dirtypipe
-cve-2022-2590 dirtyc0w_shmem
-cve-2022-23222 bpf_prog07
-cve-2023-1829 tcindex01
-cve-2023-0461 setsockopt10
-cve-2023-31248 nft02
-# Tests below may cause kernel memory leak
-cve-2020-25704 perf_event_open03
-cve-2022-0185 fsconfig03
-cve-2022-4378 cve-2022-4378
-cve-2025-38236 cve-2025-38236
-cve-2025-21756 cve-2025-21756
-cve-2026-31431 af_alg08
-cve-2026-43284 xfrm01
-cve-2026-43494 io_uring04
-cve-2026-46300 xfrm02
-cve-2026-46300-skb-segment xfrm03
--
2.53.0
More information about the ltp
mailing list