Merge tag 'linux_kselftest-next-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest

Pull kselftest updates from Shuah Khan:

 - fixes, reporting improvements, and cleanup changes to several tests

 - add support for DT_GNU_HASH to selftests/vDSO

* tag 'linux_kselftest-next-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
  selftests/rseq: Fix handling of glibc without rseq support
  selftests/resctrl: Discover SNC kernel support and adjust messages
  selftests/resctrl: Adjust effective L3 cache size with SNC enabled
  selftests/ftrace: Make uprobe test more robust against binary name
  selftests/ftrace: Fix to use remount when testing mount GID option
  selftests: tmpfs: Add kselftest support to tmpfs
  selftests: tmpfs: Add Test-skip if not run as root
  selftests: harness: fix printing of mismatch values in __EXPECT()
  selftests/ring-buffer: Add test for out-of-bound pgoff mapping
  selftests/run_kselftest.sh: Fix help string for --per-test-log
  selftests: acct: Add ksft_exit_skip if not running as root
  selftests: kselftest: Fix the wrong format specifier
  selftests: timers: clocksource-switch: Adapt progress to kselftest framework
  selftests/zram: gitignore output file
  selftests/filesystems: Add missing gitignore file
  selftests: Warn about skipped tests in result summary
  selftests: kselftest: Add ksft_test_result_xpass
  selftests/vDSO: support DT_GNU_HASH
  selftests/ipc: Remove unused variables
  selftest: media_tests: fix trivial UAF typo
This commit is contained in:
Linus Torvalds
2025-01-22 12:30:20 -08:00
25 changed files with 371 additions and 86 deletions

View File

@@ -24,7 +24,7 @@ int main(void)
// Check if test is run a root
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;
}

View File

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

View File

@@ -15,11 +15,11 @@ find_alternate_gid() {
tac /etc/group | grep -v ":$original_gid:" | head -1 | cut -d: -f3
}
mount_tracefs_with_options() {
remount_tracefs_with_options() {
local mount_point="$1"
local options="$2"
mount -t tracefs -o "$options" nodev "$mount_point"
mount -t tracefs -o "remount,$options" nodev "$mount_point"
setup
}
@@ -81,7 +81,7 @@ test_gid_mount_option() {
# Unmount existing tracefs instance and mount with new GID
unmount_tracefs "$mount_point"
mount_tracefs_with_options "$mount_point" "$new_options"
remount_tracefs_with_options "$mount_point" "$new_options"
check_gid "$mount_point" "$other_group"
@@ -92,7 +92,7 @@ test_gid_mount_option() {
# Unmount and remount with the original GID
unmount_tracefs "$mount_point"
mount_tracefs_with_options "$mount_point" "$mount_options"
remount_tracefs_with_options "$mount_point" "$mount_options"
check_gid "$mount_point" "$original_group"
}

View File

@@ -6,8 +6,10 @@
echo 0 > events/enable
echo > dynamic_events
REALBIN=`readlink -f /bin/sh`
echo 'cat /proc/$$/maps' | /bin/sh | \
grep "r-xp .*/bin/.*sh$" | \
grep "r-xp .*${REALBIN}$" | \
awk '{printf "p:myevent %s:0x%s\n", $6,$3 }' >> uprobe_events
grep -q myevent uprobe_events

View File

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

View File

@@ -18,7 +18,8 @@
* ksft_print_msg(fmt, ...);
* 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_report(result, fmt, ...);
@@ -26,6 +27,7 @@
* ksft_test_result_fail(fmt, ...);
* ksft_test_result_skip(fmt, ...);
* ksft_test_result_xfail(fmt, ...);
* ksft_test_result_xpass(fmt, ...);
* ksft_test_result_error(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)
{
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())
printf("# Planned tests != run tests (%u != %u)\n",
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);
}
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, ...)
{
int saved_errno = errno;
@@ -318,6 +339,9 @@ void ksft_test_result_code(int exit_code, const char *test_name,
case KSFT_XFAIL: \
ksft_test_result_xfail(fmt, ##__VA_ARGS__); \
break; \
case KSFT_XPASS: \
ksft_test_result_xpass(fmt, ##__VA_ARGS__); \
break; \
case KSFT_SKIP: \
ksft_test_result_skip(fmt, ##__VA_ARGS__); \
break; \
@@ -403,7 +427,7 @@ static inline __noreturn __printf(1, 2) void ksft_exit_skip(const char *msg, ...
*/
if (ksft_plan || ksft_test_num()) {
ksft_cnt.ksft_xskip++;
printf("ok %d # SKIP ", 1 + ksft_test_num());
printf("ok %u # SKIP ", 1 + ksft_test_num());
} else {
printf("1..0 # SKIP ");
}

View File

@@ -27,6 +27,9 @@ def set_plan(num_tests):
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(
f"# Totals: pass:{ksft_cnt['pass']} fail:{ksft_cnt['fail']} xfail:0 xpass:0 skip:{ksft_cnt['skip']} error:0"
)

View File

@@ -118,5 +118,9 @@ ktap_finished() {
}
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:$KTAP_CNT_XFAIL xpass:0 skip:$KTAP_CNT_SKIP error:0"
}

View File

@@ -760,33 +760,33 @@
/* Report with actual signedness to avoid weird output. */ \
switch (is_signed_type(__exp) * 2 + is_signed_type(__seen)) { \
case 0: { \
unsigned long long __exp_print = (uintptr_t)__exp; \
unsigned long long __seen_print = (uintptr_t)__seen; \
__TH_LOG("Expected %s (%llu) %s %s (%llu)", \
uintmax_t __exp_print = (uintmax_t)__exp; \
uintmax_t __seen_print = (uintmax_t)__seen; \
__TH_LOG("Expected %s (%ju) %s %s (%ju)", \
_expected_str, __exp_print, #_t, \
_seen_str, __seen_print); \
break; \
} \
case 1: { \
unsigned long long __exp_print = (uintptr_t)__exp; \
long long __seen_print = (intptr_t)__seen; \
__TH_LOG("Expected %s (%llu) %s %s (%lld)", \
uintmax_t __exp_print = (uintmax_t)__exp; \
intmax_t __seen_print = (intmax_t)__seen; \
__TH_LOG("Expected %s (%ju) %s %s (%jd)", \
_expected_str, __exp_print, #_t, \
_seen_str, __seen_print); \
break; \
} \
case 2: { \
long long __exp_print = (intptr_t)__exp; \
unsigned long long __seen_print = (uintptr_t)__seen; \
__TH_LOG("Expected %s (%lld) %s %s (%llu)", \
intmax_t __exp_print = (intmax_t)__exp; \
uintmax_t __seen_print = (uintmax_t)__seen; \
__TH_LOG("Expected %s (%jd) %s %s (%ju)", \
_expected_str, __exp_print, #_t, \
_seen_str, __seen_print); \
break; \
} \
case 3: { \
long long __exp_print = (intptr_t)__exp; \
long long __seen_print = (intptr_t)__seen; \
__TH_LOG("Expected %s (%lld) %s %s (%lld)", \
intmax_t __exp_print = (intmax_t)__exp; \
intmax_t __seen_print = (intmax_t)__seen; \
__TH_LOG("Expected %s (%jd) %s %s (%jd)", \
_expected_str, __exp_print, #_t, \
_seen_str, __seen_print); \
break; \

View File

@@ -1,5 +1,5 @@
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
unbound while ioctl is in progress.
@@ -15,11 +15,11 @@ Build media_device_test
cd tools/testing/selftests/media_tests
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:
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
seconds. The idea is when device file goes away, media devnode and cdev
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
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

@@ -8,5 +8,6 @@ TEST_GEN_PROGS := resctrl_tests
LOCAL_HDRS += $(wildcard *.h)
include ../lib.mk
CFLAGS += -I$(top_srcdir)/tools/include
$(OUTPUT)/resctrl_tests: $(wildcard *.c)

View File

@@ -169,8 +169,8 @@ static int cmt_run_test(const struct resctrl_test *test, const struct user_param
return ret;
ret = check_results(&param, span, n);
if (ret && (get_vendor() == ARCH_INTEL))
ksft_print_msg("Intel CMT may be inaccurate when Sub-NUMA Clustering is enabled. Check BIOS configuration.\n");
if (ret && (get_vendor() == ARCH_INTEL) && !snc_kernel_support())
ksft_print_msg("Kernel doesn't support Sub-NUMA Clustering but it is enabled on the system.\n");
return ret;
}

View File

@@ -201,6 +201,8 @@ static int mba_run_test(const struct resctrl_test *test, const struct user_param
return ret;
ret = check_results();
if (ret && (get_vendor() == ARCH_INTEL) && !snc_kernel_support())
ksft_print_msg("Kernel doesn't support Sub-NUMA Clustering but it is enabled on the system.\n");
return ret;
}

View File

@@ -160,8 +160,8 @@ static int mbm_run_test(const struct resctrl_test *test, const struct user_param
return ret;
ret = check_results(param.fill_buf ? param.fill_buf->buf_size : 0);
if (ret && (get_vendor() == ARCH_INTEL))
ksft_print_msg("Intel MBM may be inaccurate when Sub-NUMA Clustering is enabled. Check BIOS configuration.\n");
if (ret && (get_vendor() == ARCH_INTEL) && !snc_kernel_support())
ksft_print_msg("Kernel doesn't support Sub-NUMA Clustering but it is enabled on the system.\n");
return ret;
}

View File

@@ -11,6 +11,7 @@
#include <signal.h>
#include <dirent.h>
#include <stdbool.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
@@ -21,6 +22,7 @@
#include <sys/eventfd.h>
#include <asm/unistd.h>
#include <linux/perf_event.h>
#include <linux/compiler.h>
#include "../kselftest.h"
#define MB (1024 * 1024)
@@ -156,8 +158,11 @@ struct perf_event_read {
*/
extern volatile int *value_sink;
extern int snc_unreliable;
extern char llc_occup_path[1024];
int snc_nodes_per_l3_cache(void);
int get_vendor(void);
bool check_resctrlfs_support(void);
int filter_dmesg(void);
@@ -198,6 +203,7 @@ void ctrlc_handler(int signum, siginfo_t *info, void *ptr);
int signal_handler_register(const struct resctrl_test *test);
void signal_handler_unregister(void);
unsigned int count_bits(unsigned long n);
int snc_kernel_support(void);
void perf_event_attr_initialize(struct perf_event_attr *pea, __u64 config);
void perf_event_initialize_read_format(struct perf_event_read *pe_read);

View File

@@ -118,7 +118,7 @@ static bool test_vendor_specific_check(const struct resctrl_test *test)
static void run_single_test(const struct resctrl_test *test, const struct user_params *uparams)
{
int ret;
int ret, snc_mode;
if (test->disabled)
return;
@@ -128,8 +128,15 @@ static void run_single_test(const struct resctrl_test *test, const struct user_p
return;
}
snc_mode = snc_nodes_per_l3_cache();
ksft_print_msg("Starting %s test ...\n", test->name);
if (snc_mode == 1 && snc_unreliable && get_vendor() == ARCH_INTEL) {
ksft_test_result_skip("SNC detection unreliable due to offline CPUs. Test results may not be accurate if SNC enabled.\n");
return;
}
if (test_prepare(test)) {
ksft_exit_fail_msg("Abnormal failure when preparing for the test\n");
return;

View File

@@ -13,6 +13,8 @@
#include "resctrl.h"
int snc_unreliable;
static int find_resctrl_mount(char *buffer)
{
FILE *mounts;
@@ -156,6 +158,98 @@ int get_domain_id(const char *resource, int cpu_no, int *domain_id)
return 0;
}
/*
* Count number of CPUs in a /sys bitmap
*/
static unsigned int count_sys_bitmap_bits(char *name)
{
FILE *fp = fopen(name, "r");
int count = 0, c;
if (!fp)
return 0;
while ((c = fgetc(fp)) != EOF) {
if (!isxdigit(c))
continue;
switch (c) {
case 'f':
count++;
fallthrough;
case '7': case 'b': case 'd': case 'e':
count++;
fallthrough;
case '3': case '5': case '6': case '9': case 'a': case 'c':
count++;
fallthrough;
case '1': case '2': case '4': case '8':
count++;
break;
}
}
fclose(fp);
return count;
}
static bool cpus_offline_empty(void)
{
char offline_cpus_str[64];
FILE *fp;
fp = fopen("/sys/devices/system/cpu/offline", "r");
if (!fp) {
ksft_perror("Could not open /sys/devices/system/cpu/offline");
return 0;
}
if (fscanf(fp, "%63s", offline_cpus_str) < 0) {
if (!errno) {
fclose(fp);
return 1;
}
ksft_perror("Could not read /sys/devices/system/cpu/offline");
}
fclose(fp);
return 0;
}
/*
* Detect SNC by comparing #CPUs in node0 with #CPUs sharing LLC with CPU0.
* If any CPUs are offline declare the detection as unreliable.
*/
int snc_nodes_per_l3_cache(void)
{
int node_cpus, cache_cpus;
static int snc_mode;
if (!snc_mode) {
snc_mode = 1;
if (!cpus_offline_empty()) {
ksft_print_msg("Runtime SNC detection unreliable due to offline CPUs.\n");
ksft_print_msg("Setting SNC mode to disabled.\n");
snc_unreliable = 1;
return snc_mode;
}
node_cpus = count_sys_bitmap_bits("/sys/devices/system/node/node0/cpumap");
cache_cpus = count_sys_bitmap_bits("/sys/devices/system/cpu/cpu0/cache/index3/shared_cpu_map");
if (!node_cpus || !cache_cpus) {
ksft_print_msg("Could not determine Sub-NUMA Cluster mode.\n");
snc_unreliable = 1;
return snc_mode;
}
snc_mode = cache_cpus / node_cpus;
if (snc_mode > 1)
ksft_print_msg("SNC-%d mode discovered.\n", snc_mode);
}
return snc_mode;
}
/*
* get_cache_size - Get cache size for a specified CPU
* @cpu_no: CPU number
@@ -211,6 +305,17 @@ int get_cache_size(int cpu_no, const char *cache_type, unsigned long *cache_size
break;
}
/*
* The amount of cache represented by each bit in the masks
* in the schemata file is reduced by a factor equal to SNC
* nodes per L3 cache.
* E.g. on a SNC-2 system with a 100MB L3 cache a test that
* allocates memory from its local SNC node (default behavior
* without using libnuma) will only see 50 MB llc_occupancy
* with a fully populated L3 mask in the schemata file.
*/
if (cache_num == 3)
*cache_size /= snc_nodes_per_l3_cache();
return 0;
}
@@ -852,3 +957,35 @@ unsigned int count_bits(unsigned long n)
return count;
}
/**
* snc_kernel_support - Check for existence of mon_sub_L3_00 file that indicates
* SNC resctrl support on the kernel side.
*
* Return: 0 if not supported, 1 if SNC is disabled or SNC discovery is
* unreliable or SNC is both enabled and supported.
*/
int snc_kernel_support(void)
{
char node_path[PATH_MAX];
struct stat statbuf;
int ret;
ret = snc_nodes_per_l3_cache();
/*
* If SNC is disabled then its kernel support isn't important. If SNC
* got disabled because the discovery process was unreliable the
* snc_unreliable variable was set. It can be used to verify the SNC
* discovery reliability elsewhere in the selftest.
*/
if (ret == 1)
return ret;
snprintf(node_path, sizeof(node_path), "%s/%s", RESCTRL_PATH,
"mon_data/mon_L3_00/mon_sub_L3_00");
if (!stat(node_path, &statbuf))
return 1;
return 0;
}

View File

@@ -233,12 +233,18 @@ TEST_F(map, data_mmap)
ASSERT_NE(data, MAP_FAILED);
munmap(data, data_len);
/* Overflow the available subbufs by 1 */
/* Offset within ring-buffer bounds, mapping size overflow */
meta_len += desc->meta->subbuf_size * 2;
data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,
desc->cpu_fd, meta_len);
ASSERT_EQ(data, MAP_FAILED);
/* Offset outside ring-buffer bounds */
data_len = desc->meta->subbuf_size * desc->meta->nr_subbufs;
data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,
desc->cpu_fd, data_len + (desc->meta->subbuf_size * 2));
ASSERT_EQ(data, MAP_FAILED);
/* Verify meta-page padding */
if (desc->meta->meta_page_size > getpagesize()) {
data_len = desc->meta->meta_page_size;

View File

@@ -61,7 +61,6 @@ unsigned int rseq_size = -1U;
unsigned int rseq_flags;
static int rseq_ownership;
static int rseq_reg_success; /* At least one rseq registration has succeded. */
/* Allocate a large area for the TLS. */
#define RSEQ_THREAD_AREA_ALLOC_SIZE 1024
@@ -152,14 +151,27 @@ int rseq_register_current_thread(void)
}
rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG);
if (rc) {
if (RSEQ_READ_ONCE(rseq_reg_success)) {
/*
* After at least one thread has registered successfully
* (rseq_size > 0), the registration of other threads should
* never fail.
*/
if (RSEQ_READ_ONCE(rseq_size) > 0) {
/* Incoherent success/failure within process. */
abort();
}
return -1;
}
assert(rseq_current_cpu_raw() >= 0);
RSEQ_WRITE_ONCE(rseq_reg_success, 1);
/*
* The first thread to register sets the rseq_size to mimic the libc
* behavior.
*/
if (RSEQ_READ_ONCE(rseq_size) == 0) {
RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size());
}
return 0;
}
@@ -235,12 +247,18 @@ void rseq_init(void)
return;
}
rseq_ownership = 1;
if (!rseq_available()) {
rseq_size = 0;
return;
}
/* Calculate the offset of the rseq area from the thread pointer. */
rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer();
/* rseq flags are deprecated, always set to 0. */
rseq_flags = 0;
/*
* Set the size to 0 until at least one thread registers to mimic the
* libc behavior.
*/
rseq_size = 0;
}
static __attribute__((destructor))

View File

@@ -60,7 +60,14 @@
extern ptrdiff_t rseq_offset;
/*
* Size of the registered rseq area. 0 if the registration was
* The rseq ABI is composed of extensible feature fields. The extensions
* are done by appending additional fields at the end of the structure.
* The rseq_size defines the size of the active feature set which can be
* used by the application for the current rseq registration. Features
* starting at offset >= rseq_size are inactive and should not be used.
*
* The rseq_size is the intersection between the available allocation
* size for the rseq area and the feature size supported by the kernel.
* unsuccessful.
*/
extern unsigned int rseq_size;

View File

@@ -21,7 +21,7 @@ usage()
cat <<EOF
Usage: $0 [OPTIONS]
-s | --summary Print summary with detailed log in output.log (conflict with -p)
-p | --per_test_log Print test log in /tmp with each test name (conflict with -s)
-p | --per-test-log Print test log in /tmp with each test name (conflict with -s)
-t | --test COLLECTION:TEST Run TEST from COLLECTION
-c | --collection COLLECTION Run all tests from COLLECTION
-l | --list List the available collection:test entries

View File

@@ -156,8 +156,8 @@ int main(int argc, char **argv)
/* Check everything is sane before we start switching asynchronously */
if (do_sanity_check) {
for (i = 0; i < count; i++) {
printf("Validating clocksource %s\n",
clocksource_list[i]);
ksft_print_msg("Validating clocksource %s\n",
clocksource_list[i]);
if (change_clocksource(clocksource_list[i])) {
status = -1;
goto out;
@@ -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();
if (!pid)
return run_tests(runtime);

View File

@@ -23,45 +23,56 @@
#include <sys/mount.h>
#include <unistd.h>
#include "../kselftest.h"
int main(void)
{
int fd;
if (unshare(CLONE_NEWNS) == -1) {
if (errno == ENOSYS || errno == EPERM) {
fprintf(stderr, "error: unshare, errno %d\n", errno);
return 4;
}
fprintf(stderr, "error: unshare, errno %d\n", errno);
// Setting up kselftest framework
ksft_print_header();
ksft_set_plan(1);
// Check if test is run as root
if (geteuid()) {
ksft_exit_skip("This test needs root to run!\n");
return 1;
}
if (unshare(CLONE_NEWNS) == -1) {
if (errno == ENOSYS || errno == EPERM) {
ksft_exit_skip("unshare() error: unshare, errno %d\n", errno);
} else {
ksft_exit_fail_msg("unshare() error: unshare, errno %d\n", errno);
}
}
if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) == -1) {
fprintf(stderr, "error: mount '/', errno %d\n", errno);
return 1;
ksft_exit_fail_msg("mount() error: Root filesystem private mount: Fail %d\n", errno);
}
/* Our heroes: 1 root inode, 1 O_TMPFILE inode, 1 permanent inode. */
if (mount(NULL, "/tmp", "tmpfs", 0, "nr_inodes=3") == -1) {
fprintf(stderr, "error: mount tmpfs, errno %d\n", errno);
return 1;
ksft_exit_fail_msg("mount() error: Mounting tmpfs on /tmp: Fail %d\n", errno);
}
fd = openat(AT_FDCWD, "/tmp", O_WRONLY|O_TMPFILE, 0600);
if (fd == -1) {
fprintf(stderr, "error: open 1, errno %d\n", errno);
return 1;
ksft_exit_fail_msg("openat() error: Open first temporary file: Fail %d\n", errno);
}
if (linkat(fd, "", AT_FDCWD, "/tmp/1", AT_EMPTY_PATH) == -1) {
fprintf(stderr, "error: linkat, errno %d\n", errno);
return 1;
ksft_exit_fail_msg("linkat() error: Linking the temporary file: Fail %d\n", errno);
/* Ensure fd is closed on failure */
close(fd);
}
close(fd);
fd = openat(AT_FDCWD, "/tmp", O_WRONLY|O_TMPFILE, 0600);
if (fd == -1) {
fprintf(stderr, "error: open 2, errno %d\n", errno);
return 1;
ksft_exit_fail_msg("openat() error: Opening the second temporary file: Fail %d\n", errno);
}
ksft_test_result_pass(" ");
ksft_exit_pass();
return 0;
}

View File

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

View File

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