From 2a775d5faf40a04bd72e90d4e7ff40a43a843782 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mar 23 2023 23:13:42 +0000 Subject: Merge pull request #26935 from keszybz/test-parse_aux Add test for auxv parsing --- diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 1f259cf..b7a62e7 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -49,7 +49,6 @@ #include "sync-util.h" #include "tmpfile-util.h" #include "uid-alloc-range.h" -#include "unaligned.h" #include "user-util.h" /* The maximum size up to which we process coredumps. We use 1G on 32bit systems, and 32G on 64bit systems */ @@ -336,66 +335,6 @@ static int make_filename(const Context *context, char **ret) { return 0; } -#define _DEFINE_PARSE_AUXV(size, type, unaligned_read) \ - static int parse_auxv##size( \ - const void *auxv, \ - size_t size_bytes, \ - int *at_secure, \ - uid_t *uid, \ - uid_t *euid, \ - gid_t *gid, \ - gid_t *egid) { \ - \ - assert(auxv || size_bytes == 0); \ - \ - if (size_bytes % (2 * sizeof(type)) != 0) \ - return log_warning_errno(SYNTHETIC_ERRNO(EIO), \ - "Incomplete auxv structure (%zu bytes).", \ - size_bytes); \ - \ - size_t words = size_bytes / sizeof(type); \ - \ - /* Note that we set output variables even on error. */ \ - \ - for (size_t i = 0; i + 1 < words; i += 2) { \ - type key, val; \ - \ - key = unaligned_read((uint8_t*) auxv + i * sizeof(type)); \ - val = unaligned_read((uint8_t*) auxv + (i + 1) * sizeof(type)); \ - \ - switch (key) { \ - case AT_SECURE: \ - *at_secure = val != 0; \ - break; \ - case AT_UID: \ - *uid = val; \ - break; \ - case AT_EUID: \ - *euid = val; \ - break; \ - case AT_GID: \ - *gid = val; \ - break; \ - case AT_EGID: \ - *egid = val; \ - break; \ - case AT_NULL: \ - if (val != 0) \ - goto error; \ - return 0; \ - } \ - } \ - error: \ - return log_warning_errno(SYNTHETIC_ERRNO(ENODATA), \ - "AT_NULL terminator not found, cannot parse auxv structure."); \ - } - -#define DEFINE_PARSE_AUXV(size)\ - _DEFINE_PARSE_AUXV(size, uint##size##_t, unaligned_read_ne##size) - -DEFINE_PARSE_AUXV(32); -DEFINE_PARSE_AUXV(64); - static int grant_user_access(int core_fd, const Context *context) { int at_secure = -1; uid_t uid = UID_INVALID, euid = UID_INVALID; @@ -430,14 +369,11 @@ static int grant_user_access(int core_fd, const Context *context) { return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), "Core file has non-native endianness, not adjusting permissions."); - if (elf[EI_CLASS] == ELFCLASS64) - r = parse_auxv64(context->meta[META_PROC_AUXV], - context->meta_size[META_PROC_AUXV], - &at_secure, &uid, &euid, &gid, &egid); - else - r = parse_auxv32(context->meta[META_PROC_AUXV], - context->meta_size[META_PROC_AUXV], - &at_secure, &uid, &euid, &gid, &egid); + r = parse_auxv(LOG_WARNING, + /* elf_class= */ elf[EI_CLASS], + context->meta[META_PROC_AUXV], + context->meta_size[META_PROC_AUXV], + &at_secure, &uid, &euid, &gid, &egid); if (r < 0) return r; diff --git a/src/shared/coredump-util.c b/src/shared/coredump-util.c index 3d2f179..bf8ea00 100644 --- a/src/shared/coredump-util.c +++ b/src/shared/coredump-util.c @@ -1,9 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "coredump-util.h" #include "extract-word.h" #include "fileio.h" #include "string-table.h" +#include "unaligned.h" #include "virt.h" static const char *const coredump_filter_table[_COREDUMP_FILTER_MAX] = { @@ -65,6 +68,95 @@ int coredump_filter_mask_from_string(const char *s, uint64_t *ret) { return 0; } +#define _DEFINE_PARSE_AUXV(size, type, unaligned_read) \ + static int parse_auxv##size( \ + int log_level, \ + const void *auxv, \ + size_t size_bytes, \ + int *at_secure, \ + uid_t *uid, \ + uid_t *euid, \ + gid_t *gid, \ + gid_t *egid) { \ + \ + assert(auxv || size_bytes == 0); \ + assert(at_secure); \ + assert(uid); \ + assert(euid); \ + assert(gid); \ + assert(egid); \ + \ + if (size_bytes % (2 * sizeof(type)) != 0) \ + return log_full_errno(log_level, \ + SYNTHETIC_ERRNO(EIO), \ + "Incomplete auxv structure (%zu bytes).", \ + size_bytes); \ + \ + size_t words = size_bytes / sizeof(type); \ + \ + /* Note that we set output variables even on error. */ \ + \ + for (size_t i = 0; i + 1 < words; i += 2) { \ + type key, val; \ + \ + key = unaligned_read((uint8_t*) auxv + i * sizeof(type)); \ + val = unaligned_read((uint8_t*) auxv + (i + 1) * sizeof(type)); \ + \ + switch (key) { \ + case AT_SECURE: \ + *at_secure = val != 0; \ + break; \ + case AT_UID: \ + *uid = val; \ + break; \ + case AT_EUID: \ + *euid = val; \ + break; \ + case AT_GID: \ + *gid = val; \ + break; \ + case AT_EGID: \ + *egid = val; \ + break; \ + case AT_NULL: \ + if (val != 0) \ + goto error; \ + return 0; \ + } \ + } \ + error: \ + return log_full_errno(log_level, \ + SYNTHETIC_ERRNO(ENODATA), \ + "AT_NULL terminator not found, cannot parse auxv structure."); \ + } + +#define DEFINE_PARSE_AUXV(size) \ + _DEFINE_PARSE_AUXV(size, uint##size##_t, unaligned_read_ne##size) + +DEFINE_PARSE_AUXV(32); +DEFINE_PARSE_AUXV(64); + +int parse_auxv(int log_level, + uint8_t elf_class, + const void *auxv, + size_t size_bytes, + int *at_secure, + uid_t *uid, + uid_t *euid, + gid_t *gid, + gid_t *egid) { + + switch (elf_class) { + case ELFCLASS64: + return parse_auxv64(log_level, auxv, size_bytes, at_secure, uid, euid, gid, egid); + case ELFCLASS32: + return parse_auxv32(log_level, auxv, size_bytes, at_secure, uid, euid, gid, egid); + default: + return log_full_errno(log_level, SYNTHETIC_ERRNO(EPROTONOSUPPORT), + "Unknown ELF class %d.", elf_class); + } +} + int set_coredump_filter(uint64_t value) { char t[STRLEN("0xFFFFFFFF")]; diff --git a/src/shared/coredump-util.h b/src/shared/coredump-util.h index 8eda86d..99dbfde 100644 --- a/src/shared/coredump-util.h +++ b/src/shared/coredump-util.h @@ -26,5 +26,15 @@ const char* coredump_filter_to_string(CoredumpFilter i) _const_; CoredumpFilter coredump_filter_from_string(const char *s) _pure_; int coredump_filter_mask_from_string(const char *s, uint64_t *ret); +int parse_auxv(int log_level, + uint8_t elf_class, + const void *auxv, + size_t size_bytes, + int *at_secure, + uid_t *uid, + uid_t *euid, + gid_t *gid, + gid_t *egid); + int set_coredump_filter(uint64_t value); void disable_coredumps(void); diff --git a/src/test/test-coredump-util.c b/src/test/test-coredump-util.c index 40b68df..7a41e0f 100644 --- a/src/test/test-coredump-util.c +++ b/src/test/test-coredump-util.c @@ -1,7 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "alloc-util.h" #include "coredump-util.h" +#include "fileio.h" +#include "fd-util.h" +#include "format-util.h" #include "macro.h" #include "tests.h" @@ -64,4 +69,91 @@ TEST(coredump_filter_mask_from_string) { 1 << COREDUMP_FILTER_SHARED_DAX))); } +static void test_parse_auxv_two( + uint8_t elf_class, + size_t offset, + const char *data, + size_t data_size, + int expect_at_secure, + uid_t expect_uid, + uid_t expect_euid, + gid_t expect_gid, + gid_t expect_egid) { + + int at_secure; + uid_t uid, euid; + gid_t gid, egid; + assert_se(parse_auxv(LOG_ERR, elf_class, data, data_size, + &at_secure, &uid, &euid, &gid, &egid) == 0); + + log_debug("[offset=%zu] at_secure=%d, uid="UID_FMT", euid="UID_FMT", gid="GID_FMT", egid="GID_FMT, + offset, + at_secure, uid, euid, gid, egid); + + assert_se(uid == expect_uid); + assert_se(euid == expect_euid); + assert_se(gid == expect_gid); + assert_se(egid == expect_egid); +} + +static void test_parse_auxv_one( + uint8_t elf_class, + int dir_fd, + const char *filename, + int expect_at_secure, + uid_t expect_uid, + uid_t expect_euid, + gid_t expect_gid, + gid_t expect_egid) { + + _cleanup_free_ char *buf; + const char *data; + size_t data_size; + log_info("Parsing %s…", filename); + assert_se(read_full_file_at(dir_fd, filename, &buf, &data_size) >= 0); + + for (size_t offset = 0; offset < 8; offset++) { + _cleanup_free_ char *buf2 = NULL; + + if (offset == 0) + data = buf; + else { + assert_se(buf2 = malloc(offset + data_size)); + memcpy(buf2 + offset, buf, data_size); + data = buf2 + offset; + } + + test_parse_auxv_two(elf_class, offset, data, data_size, + expect_at_secure, expect_uid, expect_euid, expect_gid, expect_egid); + } +} + +TEST(test_parse_auxv) { + _cleanup_free_ char *dir = NULL; + _cleanup_close_ int dir_fd = -EBADF; + + assert_se(get_testdata_dir("auxv", &dir) >= 0); + dir_fd = open(dir, O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_PATH); + assert_se(dir_fd >= 0); + + if (__BYTE_ORDER == __LITTLE_ENDIAN) { + test_parse_auxv_one(ELFCLASS32, dir_fd, "resolved.arm32", 0, 193, 193, 193, 193); + test_parse_auxv_one(ELFCLASS64, dir_fd, "bash.riscv64", 0, 1001, 1001, 1001, 1001); + test_parse_auxv_one(ELFCLASS32, dir_fd, "sleep.i686", 0, 1000, 1000, 1000, 1000); + /* after chgrp and chmod g+s */ + test_parse_auxv_one(ELFCLASS32, dir_fd, "sleep32.i686", 1, 1000, 1000, 1000, 10); + test_parse_auxv_one(ELFCLASS64, dir_fd, "sleep64.amd64", 1, 1000, 1000, 1000, 10); + + test_parse_auxv_one(ELFCLASS64, dir_fd, "sudo.aarch64", 1, 1494200408, 0, 1494200408, 1494200408); + test_parse_auxv_one(ELFCLASS64, dir_fd, "sudo.amd64", 1, 1000, 0, 1000, 1000); + + /* Those run unprivileged, but start as root. */ + test_parse_auxv_one(ELFCLASS64, dir_fd, "dbus-broker-launch.amd64", 0, 0, 0, 0, 0); + test_parse_auxv_one(ELFCLASS64, dir_fd, "dbus-broker-launch.aarch64", 0, 0, 0, 0, 0); + test_parse_auxv_one(ELFCLASS64, dir_fd, "polkitd.aarch64", 0, 0, 0, 0, 0); + } else { + test_parse_auxv_one(ELFCLASS64, dir_fd, "cat.s390x", 0, 3481, 3481, 3481, 3481); + } +} + DEFINE_TEST_MAIN(LOG_INFO); diff --git a/test/auxv/.gitattributes b/test/auxv/.gitattributes new file mode 100644 index 0000000..58e3ff4 --- /dev/null +++ b/test/auxv/.gitattributes @@ -0,0 +1,3 @@ +/*.* -whitespace +/*.* binary +/*.* generated diff --git a/test/auxv/bash.riscv64 b/test/auxv/bash.riscv64 new file mode 100644 index 0000000..273a468 Binary files /dev/null and b/test/auxv/bash.riscv64 differ diff --git a/test/auxv/cat.s390x b/test/auxv/cat.s390x new file mode 100644 index 0000000..aa76441 Binary files /dev/null and b/test/auxv/cat.s390x differ diff --git a/test/auxv/dbus-broker-launch.aarch64 b/test/auxv/dbus-broker-launch.aarch64 new file mode 100644 index 0000000..3a05e3c Binary files /dev/null and b/test/auxv/dbus-broker-launch.aarch64 differ diff --git a/test/auxv/dbus-broker-launch.amd64 b/test/auxv/dbus-broker-launch.amd64 new file mode 100644 index 0000000..21965e8 Binary files /dev/null and b/test/auxv/dbus-broker-launch.amd64 differ diff --git a/test/auxv/polkitd.aarch64 b/test/auxv/polkitd.aarch64 new file mode 100644 index 0000000..ff1ea9f Binary files /dev/null and b/test/auxv/polkitd.aarch64 differ diff --git a/test/auxv/resolved.arm32 b/test/auxv/resolved.arm32 new file mode 100644 index 0000000..7d05f3b Binary files /dev/null and b/test/auxv/resolved.arm32 differ diff --git a/test/auxv/sleep.i686 b/test/auxv/sleep.i686 new file mode 100644 index 0000000..d0b5f2c Binary files /dev/null and b/test/auxv/sleep.i686 differ diff --git a/test/auxv/sleep32.i686 b/test/auxv/sleep32.i686 new file mode 100644 index 0000000..f52f512 Binary files /dev/null and b/test/auxv/sleep32.i686 differ diff --git a/test/auxv/sleep64.amd64 b/test/auxv/sleep64.amd64 new file mode 100644 index 0000000..c3c7ed4 Binary files /dev/null and b/test/auxv/sleep64.amd64 differ diff --git a/test/auxv/sudo.aarch64 b/test/auxv/sudo.aarch64 new file mode 100644 index 0000000..e49ce6a Binary files /dev/null and b/test/auxv/sudo.aarch64 differ diff --git a/test/auxv/sudo.amd64 b/test/auxv/sudo.amd64 new file mode 100644 index 0000000..91e4646 Binary files /dev/null and b/test/auxv/sudo.amd64 differ diff --git a/test/meson.build b/test/meson.build index 1135ecd..1721cff 100644 --- a/test/meson.build +++ b/test/meson.build @@ -3,63 +3,32 @@ if install_tests testdata_dir = testsdir + '/testdata/' - install_subdir('journal-data', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('test-execute', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('test-fstab-generator', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('test-path', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('test-path-util', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('test-umount', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('test-network-generator-conversion', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-03.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-04.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-06.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-10.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-11.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-16.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-28.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-30.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-52.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-63.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) - install_subdir('testsuite-80.units', - exclude_files : '.gitattributes', - install_dir : testdata_dir) + foreach subdir : [ + 'auxv', + 'journal-data', + 'units', + 'test-execute', + 'test-fstab-generator', + 'test-path', + 'test-path-util', + 'test-umount', + 'test-network-generator-conversion', + 'testsuite-03.units', + 'testsuite-04.units', + 'testsuite-06.units', + 'testsuite-10.units', + 'testsuite-11.units', + 'testsuite-16.units', + 'testsuite-28.units', + 'testsuite-30.units', + 'testsuite-52.units', + 'testsuite-63.units', + 'testsuite-80.units', + ] + install_subdir(subdir, + exclude_files : '.gitattributes', + install_dir : testdata_dir) + endforeach install_data(kbd_model_map, install_dir : testdata_dir + '/test-keymap-util')