Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git

This commit is contained in:
Stephen Rothwell 2024-12-20 14:45:47 +11:00
commit 410a2fb2e3
10 changed files with 125 additions and 37 deletions

View File

@ -24,7 +24,7 @@ int main(void)
// Check if test is run a root // Check if test is run a root
if (geteuid()) { if (geteuid()) {
ksft_test_result_skip("This test needs root to run!\n"); ksft_exit_skip("This test needs root to run!\n");
return 1; return 1;
} }

View File

@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
statmount_test_ns
/*_test /*_test

View File

@ -194,7 +194,7 @@ int fill_msgque(struct msgque_data *msgque)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int msg, pid, err; int err;
struct msgque_data msgque; struct msgque_data msgque;
if (getuid() != 0) if (getuid() != 0)

View File

@ -18,7 +18,8 @@
* ksft_print_msg(fmt, ...); * ksft_print_msg(fmt, ...);
* ksft_perror(msg); * ksft_perror(msg);
* *
* and finally report the pass/fail/skip/xfail state of the test with one of: * and finally report the pass/fail/skip/xfail/xpass state of the test
* with one of:
* *
* ksft_test_result(condition, fmt, ...); * ksft_test_result(condition, fmt, ...);
* ksft_test_result_report(result, fmt, ...); * ksft_test_result_report(result, fmt, ...);
@ -26,6 +27,7 @@
* ksft_test_result_fail(fmt, ...); * ksft_test_result_fail(fmt, ...);
* ksft_test_result_skip(fmt, ...); * ksft_test_result_skip(fmt, ...);
* ksft_test_result_xfail(fmt, ...); * ksft_test_result_xfail(fmt, ...);
* ksft_test_result_xpass(fmt, ...);
* ksft_test_result_error(fmt, ...); * ksft_test_result_error(fmt, ...);
* ksft_test_result_code(exit_code, test_name, fmt, ...); * ksft_test_result_code(exit_code, test_name, fmt, ...);
* *
@ -147,6 +149,11 @@ static inline void ksft_set_plan(unsigned int plan)
static inline void ksft_print_cnts(void) static inline void ksft_print_cnts(void)
{ {
if (ksft_cnt.ksft_xskip > 0)
printf(
"# %u skipped test(s) detected. Consider enabling relevant config options to improve coverage.\n",
ksft_cnt.ksft_xskip
);
if (ksft_plan != ksft_test_num()) if (ksft_plan != ksft_test_num())
printf("# Planned tests != run tests (%u != %u)\n", printf("# Planned tests != run tests (%u != %u)\n",
ksft_plan, ksft_test_num()); ksft_plan, ksft_test_num());
@ -227,6 +234,20 @@ static inline __printf(1, 2) void ksft_test_result_xfail(const char *msg, ...)
va_end(args); va_end(args);
} }
static inline __printf(1, 2) void ksft_test_result_xpass(const char *msg, ...)
{
int saved_errno = errno;
va_list args;
ksft_cnt.ksft_xpass++;
va_start(args, msg);
printf("ok %u # XPASS ", ksft_test_num());
errno = saved_errno;
vprintf(msg, args);
va_end(args);
}
static inline __printf(1, 2) void ksft_test_result_skip(const char *msg, ...) static inline __printf(1, 2) void ksft_test_result_skip(const char *msg, ...)
{ {
int saved_errno = errno; int saved_errno = errno;
@ -318,6 +339,9 @@ void ksft_test_result_code(int exit_code, const char *test_name,
case KSFT_XFAIL: \ case KSFT_XFAIL: \
ksft_test_result_xfail(fmt, ##__VA_ARGS__); \ ksft_test_result_xfail(fmt, ##__VA_ARGS__); \
break; \ break; \
case KSFT_XPASS: \
ksft_test_result_xpass(fmt, ##__VA_ARGS__); \
break; \
case KSFT_SKIP: \ case KSFT_SKIP: \
ksft_test_result_skip(fmt, ##__VA_ARGS__); \ ksft_test_result_skip(fmt, ##__VA_ARGS__); \
break; \ break; \
@ -403,7 +427,7 @@ static inline __noreturn __printf(1, 2) void ksft_exit_skip(const char *msg, ...
*/ */
if (ksft_plan || ksft_test_num()) { if (ksft_plan || ksft_test_num()) {
ksft_cnt.ksft_xskip++; ksft_cnt.ksft_xskip++;
printf("ok %d # SKIP ", 1 + ksft_test_num()); printf("ok %u # SKIP ", 1 + ksft_test_num());
} else { } else {
printf("1..0 # SKIP "); printf("1..0 # SKIP ");
} }

View File

@ -27,6 +27,9 @@ def set_plan(num_tests):
def print_cnts(): def print_cnts():
if ksft_cnt['skip'] > 0:
print(f"# {ksft_cnt['skip']} skipped test(s) detected. Consider enabling relevant config options to improve coverage.")
print( print(
f"# Totals: pass:{ksft_cnt['pass']} fail:{ksft_cnt['fail']} xfail:0 xpass:0 skip:{ksft_cnt['skip']} error:0" f"# Totals: pass:{ksft_cnt['pass']} fail:{ksft_cnt['fail']} xfail:0 xpass:0 skip:{ksft_cnt['skip']} error:0"
) )

View File

@ -107,5 +107,9 @@ ktap_finished() {
} }
ktap_print_totals() { ktap_print_totals() {
if [ "$KTAP_CNT_SKIP" -gt 0 ]; then
echo "# $KTAP_CNT_SKIP skipped test(s) detected. " \
"Consider enabling relevant config options to improve coverage."
fi
echo "# Totals: pass:$KTAP_CNT_PASS fail:$KTAP_CNT_FAIL xfail:0 xpass:0 skip:$KTAP_CNT_SKIP error:0" echo "# Totals: pass:$KTAP_CNT_PASS fail:$KTAP_CNT_FAIL xfail:0 xpass:0 skip:$KTAP_CNT_SKIP error:0"
} }

View File

@ -1,5 +1,5 @@
Testing for regressions in Media Controller API register, ioctl, syscall, Testing for regressions in Media Controller API register, ioctl, syscall,
and unregister paths. There have a few problems that result in user-after and unregister paths. There have a few problems that result in use-after
free on media_device, media_devnode, and cdev pointers when the driver is free on media_device, media_devnode, and cdev pointers when the driver is
unbound while ioctl is in progress. unbound while ioctl is in progress.
@ -15,11 +15,11 @@ Build media_device_test
cd tools/testing/selftests/media_tests cd tools/testing/selftests/media_tests
make make
Regressions test for cdev user-after free error on /dev/mediaX when driver Regressions test for cdev use-after-free error on /dev/mediaX when driver
is unbound: is unbound:
Start media_device_test to regression test media devnode dynamic alloc Start media_device_test to regression test media devnode dynamic alloc
and cdev user-after-free fixes. This opens media dev files and sits in and cdev use-after-free fixes. This opens media dev files and sits in
a loop running media ioctl MEDIA_IOC_DEVICE_INFO command once every 10 a loop running media ioctl MEDIA_IOC_DEVICE_INFO command once every 10
seconds. The idea is when device file goes away, media devnode and cdev seconds. The idea is when device file goes away, media devnode and cdev
should stick around until this test exits. should stick around until this test exits.
@ -40,4 +40,4 @@ keep ioctls going while bind/unbind runs.
Copy bind_unbind_sample.txt and make changes to specify the driver name Copy bind_unbind_sample.txt and make changes to specify the driver name
and number to run bind and unbind. Start the bind_unbind.sh and number to run bind and unbind. Start the bind_unbind.sh
Run dmesg looking for any user-after free errors or mutex lock errors. Run dmesg looking for any use-after-free errors or mutex lock errors.

View File

@ -156,7 +156,7 @@ int main(int argc, char **argv)
/* Check everything is sane before we start switching asynchronously */ /* Check everything is sane before we start switching asynchronously */
if (do_sanity_check) { if (do_sanity_check) {
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
printf("Validating clocksource %s\n", ksft_print_msg("Validating clocksource %s\n",
clocksource_list[i]); clocksource_list[i]);
if (change_clocksource(clocksource_list[i])) { if (change_clocksource(clocksource_list[i])) {
status = -1; status = -1;
@ -169,7 +169,7 @@ int main(int argc, char **argv)
} }
} }
printf("Running Asynchronous Switching Tests...\n"); ksft_print_msg("Running Asynchronous Switching Tests...\n");
pid = fork(); pid = fork();
if (!pid) if (!pid)
return run_tests(runtime); return run_tests(runtime);

View File

@ -53,6 +53,7 @@ static struct vdso_info
/* Symbol table */ /* Symbol table */
ELF(Sym) *symtab; ELF(Sym) *symtab;
const char *symstrings; const char *symstrings;
ELF(Word) *gnu_hash;
ELF_HASH_ENTRY *bucket, *chain; ELF_HASH_ENTRY *bucket, *chain;
ELF_HASH_ENTRY nbucket, nchain; ELF_HASH_ENTRY nbucket, nchain;
@ -81,6 +82,16 @@ static unsigned long elf_hash(const char *name)
return h; return h;
} }
static uint32_t gnu_hash(const char *name)
{
const unsigned char *s = (void *)name;
uint32_t h = 5381;
for (; *s; s++)
h += h * 32 + *s;
return h;
}
void vdso_init_from_sysinfo_ehdr(uintptr_t base) void vdso_init_from_sysinfo_ehdr(uintptr_t base)
{ {
size_t i; size_t i;
@ -123,6 +134,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
*/ */
ELF_HASH_ENTRY *hash = 0; ELF_HASH_ENTRY *hash = 0;
vdso_info.symstrings = 0; vdso_info.symstrings = 0;
vdso_info.gnu_hash = 0;
vdso_info.symtab = 0; vdso_info.symtab = 0;
vdso_info.versym = 0; vdso_info.versym = 0;
vdso_info.verdef = 0; vdso_info.verdef = 0;
@ -143,6 +155,11 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
((uintptr_t)dyn[i].d_un.d_ptr ((uintptr_t)dyn[i].d_un.d_ptr
+ vdso_info.load_offset); + vdso_info.load_offset);
break; break;
case DT_GNU_HASH:
vdso_info.gnu_hash =
(ELF(Word) *)((uintptr_t)dyn[i].d_un.d_ptr +
vdso_info.load_offset);
break;
case DT_VERSYM: case DT_VERSYM:
vdso_info.versym = (ELF(Versym) *) vdso_info.versym = (ELF(Versym) *)
((uintptr_t)dyn[i].d_un.d_ptr ((uintptr_t)dyn[i].d_un.d_ptr
@ -155,17 +172,27 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
break; break;
} }
} }
if (!vdso_info.symstrings || !vdso_info.symtab || !hash) if (!vdso_info.symstrings || !vdso_info.symtab ||
(!hash && !vdso_info.gnu_hash))
return; /* Failed */ return; /* Failed */
if (!vdso_info.verdef) if (!vdso_info.verdef)
vdso_info.versym = 0; vdso_info.versym = 0;
/* Parse the hash table header. */ /* Parse the hash table header. */
if (vdso_info.gnu_hash) {
vdso_info.nbucket = vdso_info.gnu_hash[0];
/* The bucket array is located after the header (4 uint32) and the bloom
* filter (size_t array of gnu_hash[2] elements).
*/
vdso_info.bucket = vdso_info.gnu_hash + 4 +
sizeof(size_t) / 4 * vdso_info.gnu_hash[2];
} else {
vdso_info.nbucket = hash[0]; vdso_info.nbucket = hash[0];
vdso_info.nchain = hash[1]; vdso_info.nchain = hash[1];
vdso_info.bucket = &hash[2]; vdso_info.bucket = &hash[2];
vdso_info.chain = &hash[vdso_info.nbucket + 2]; vdso_info.chain = &hash[vdso_info.nbucket + 2];
}
/* That's all we need. */ /* That's all we need. */
vdso_info.valid = true; vdso_info.valid = true;
@ -209,6 +236,26 @@ static bool vdso_match_version(ELF(Versym) ver,
&& !strcmp(name, vdso_info.symstrings + aux->vda_name); && !strcmp(name, vdso_info.symstrings + aux->vda_name);
} }
static bool check_sym(ELF(Sym) *sym, ELF(Word) i, const char *name,
const char *version, unsigned long ver_hash)
{
/* Check for a defined global or weak function w/ right name. */
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
return false;
if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
ELF64_ST_BIND(sym->st_info) != STB_WEAK)
return false;
if (strcmp(name, vdso_info.symstrings + sym->st_name))
return false;
/* Check symbol version. */
if (vdso_info.versym &&
!vdso_match_version(vdso_info.versym[i], version, ver_hash))
return false;
return true;
}
void *vdso_sym(const char *version, const char *name) void *vdso_sym(const char *version, const char *name)
{ {
unsigned long ver_hash; unsigned long ver_hash;
@ -216,29 +263,36 @@ void *vdso_sym(const char *version, const char *name)
return 0; return 0;
ver_hash = elf_hash(version); ver_hash = elf_hash(version);
ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; ELF(Word) i;
for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) { if (vdso_info.gnu_hash) {
ELF(Sym) *sym = &vdso_info.symtab[chain]; uint32_t h1 = gnu_hash(name), h2, *hashval;
/* Check for a defined global or weak function w/ right name. */ i = vdso_info.bucket[h1 % vdso_info.nbucket];
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) if (i == 0)
continue; return 0;
if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && h1 |= 1;
ELF64_ST_BIND(sym->st_info) != STB_WEAK) hashval = vdso_info.bucket + vdso_info.nbucket +
continue; (i - vdso_info.gnu_hash[1]);
if (sym->st_shndx == SHN_UNDEF) for (;; i++) {
continue; ELF(Sym) *sym = &vdso_info.symtab[i];
if (strcmp(name, vdso_info.symstrings + sym->st_name)) h2 = *hashval++;
continue; if (h1 == (h2 | 1) &&
check_sym(sym, i, name, version, ver_hash))
/* Check symbol version. */ return (void *)(vdso_info.load_offset +
if (vdso_info.versym sym->st_value);
&& !vdso_match_version(vdso_info.versym[chain], if (h2 & 1)
version, ver_hash)) break;
continue; }
} else {
return (void *)(vdso_info.load_offset + sym->st_value); i = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
for (; i; i = vdso_info.chain[i]) {
ELF(Sym) *sym = &vdso_info.symtab[i];
if (sym->st_shndx != SHN_UNDEF &&
check_sym(sym, i, name, version, ver_hash))
return (void *)(vdso_info.load_offset +
sym->st_value);
}
} }
return 0; return 0;

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
err.log