Merge pull request #18863 from keszybz/cmdline-escaping

Escape command lines properly
This commit is contained in:
Lennart Poettering
2021-05-07 17:29:39 +02:00
committed by GitHub
23 changed files with 628 additions and 322 deletions
+54 -43
View File
@@ -24,13 +24,14 @@ static void test_xescape_full(bool eight_bits) {
"a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb" :
"a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\177\234\313";
const unsigned full_fit = !eight_bits ? 55 : 46;
XEscapeFlags flags = eight_bits * XESCAPE_8_BIT;
for (unsigned i = 0; i < 60; i++) {
_cleanup_free_ char *t;
_cleanup_free_ char *t, *q;
assert_se(t = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i, eight_bits));
assert_se(t = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i, flags));
log_info("%02d: %s", i, t);
log_info("%02d: <%s>", i, t);
if (i >= full_fit)
assert_se(streq(t, escaped));
@@ -44,6 +45,15 @@ static void test_xescape_full(bool eight_bits) {
assert_se(strlen(t) == i);
assert_se(strneq(t, "...", i));
}
assert_se(q = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i,
flags | XESCAPE_FORCE_ELLIPSIS));
log_info("%02d: <%s>", i, q);
if (i > 0)
assert_se(endswith(q, "."));
assert(strlen(q) <= i);
assert(strlen(q) + 3 >= strlen(t));
}
}
@@ -118,6 +128,7 @@ static void test_shell_escape_one(const char *s, const char *bad, const char *ex
_cleanup_free_ char *r;
assert_se(r = shell_escape(s, bad));
log_debug("%s → %s (expected %s)", s, r, expected);
assert_se(streq_ptr(r, expected));
}
@@ -127,58 +138,58 @@ static void test_shell_escape(void) {
test_shell_escape_one("foobar", "", "foobar");
test_shell_escape_one("foobar", "o", "f\\o\\obar");
test_shell_escape_one("foo:bar,baz", ",:", "foo\\:bar\\,baz");
test_shell_escape_one("foo\nbar\nbaz", ",:", "foo\\nbar\\nbaz");
}
static void test_shell_maybe_quote_one(const char *s,
EscapeStyle style,
const char *expected) {
static void test_shell_maybe_quote_one(const char *s, ShellEscapeFlags flags, const char *expected) {
_cleanup_free_ char *ret = NULL;
assert_se(ret = shell_maybe_quote(s, style));
assert_se(ret = shell_maybe_quote(s, flags));
log_debug("[%s] → [%s] (%s)", s, ret, expected);
assert_se(streq(ret, expected));
}
static void test_shell_maybe_quote(void) {
test_shell_maybe_quote_one("", ESCAPE_BACKSLASH, "");
test_shell_maybe_quote_one("", ESCAPE_BACKSLASH_ONELINE, "");
test_shell_maybe_quote_one("", ESCAPE_POSIX, "");
test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH, "\"\\\\\"");
test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH_ONELINE, "\"\\\\\"");
test_shell_maybe_quote_one("\\", ESCAPE_POSIX, "$'\\\\'");
test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH, "\"\\\"\"");
test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH_ONELINE, "\"\\\"\"");
test_shell_maybe_quote_one("\"", ESCAPE_POSIX, "$'\"'");
test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH, "foobar");
test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH_ONELINE, "foobar");
test_shell_maybe_quote_one("foobar", ESCAPE_POSIX, "foobar");
test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH, "\"foo bar\"");
test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH_ONELINE, "\"foo bar\"");
test_shell_maybe_quote_one("foo bar", ESCAPE_POSIX, "$'foo bar'");
test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH, "\"foo\tbar\"");
test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\tbar\"");
test_shell_maybe_quote_one("foo\tbar", ESCAPE_POSIX, "$'foo\\tbar'");
test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH, "\"foo\nbar\"");
test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\nbar\"");
test_shell_maybe_quote_one("foo\nbar", ESCAPE_POSIX, "$'foo\\nbar'");
test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH, "\"foo \\\"bar\\\" waldo\"");
test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH_ONELINE, "\"foo \\\"bar\\\" waldo\"");
test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_POSIX, "$'foo \"bar\" waldo'");
test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH, "\"foo\\$bar\"");
test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\$bar\"");
test_shell_maybe_quote_one("foo$bar", ESCAPE_POSIX, "$'foo$bar'");
test_shell_maybe_quote_one("", 0, "");
test_shell_maybe_quote_one("", SHELL_ESCAPE_EMPTY, "\"\"");
test_shell_maybe_quote_one("", SHELL_ESCAPE_POSIX, "");
test_shell_maybe_quote_one("", SHELL_ESCAPE_POSIX | SHELL_ESCAPE_EMPTY, "\"\"");
test_shell_maybe_quote_one("\\", 0, "\"\\\\\"");
test_shell_maybe_quote_one("\\", SHELL_ESCAPE_POSIX, "$'\\\\'");
test_shell_maybe_quote_one("\"", 0, "\"\\\"\"");
test_shell_maybe_quote_one("\"", SHELL_ESCAPE_POSIX, "$'\"'");
test_shell_maybe_quote_one("foobar", 0, "foobar");
test_shell_maybe_quote_one("foobar", SHELL_ESCAPE_POSIX, "foobar");
test_shell_maybe_quote_one("foo bar", 0, "\"foo bar\"");
test_shell_maybe_quote_one("foo bar", SHELL_ESCAPE_POSIX, "$'foo bar'");
test_shell_maybe_quote_one("foo\tbar", 0, "\"foo\\tbar\"");
test_shell_maybe_quote_one("foo\tbar", SHELL_ESCAPE_POSIX, "$'foo\\tbar'");
test_shell_maybe_quote_one("foo\nbar", 0, "\"foo\\nbar\"");
test_shell_maybe_quote_one("foo\nbar", SHELL_ESCAPE_POSIX, "$'foo\\nbar'");
test_shell_maybe_quote_one("foo \"bar\" waldo", 0, "\"foo \\\"bar\\\" waldo\"");
test_shell_maybe_quote_one("foo \"bar\" waldo", SHELL_ESCAPE_POSIX, "$'foo \"bar\" waldo'");
test_shell_maybe_quote_one("foo$bar", 0, "\"foo\\$bar\"");
test_shell_maybe_quote_one("foo$bar", SHELL_ESCAPE_EMPTY, "\"foo\\$bar\"");
test_shell_maybe_quote_one("foo$bar", SHELL_ESCAPE_POSIX, "$'foo$bar'");
test_shell_maybe_quote_one("foo$bar", SHELL_ESCAPE_POSIX | SHELL_ESCAPE_EMPTY, "$'foo$bar'");
/* Note that current users disallow control characters, so this "test"
* is here merely to establish current behaviour. If control characters
* were allowed, they should be quoted, i.e. \001 should become \\001. */
test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH, "\"a\nb\001\"");
test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH_ONELINE, "\"a\\nb\001\"");
test_shell_maybe_quote_one("a\nb\001", ESCAPE_POSIX, "$'a\\nb\001'");
/* Exclamation mark is special in the interactive shell, but we don't treat it so. */
test_shell_maybe_quote_one("foo!bar", 0, "\"foo!bar\"");
test_shell_maybe_quote_one("foo!bar", SHELL_ESCAPE_POSIX, "$'foo!bar'");
test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH, "\"foo!bar\"");
test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH_ONELINE, "\"foo!bar\"");
test_shell_maybe_quote_one("foo!bar", ESCAPE_POSIX, "$'foo!bar'");
/* Control characters and unicode */
test_shell_maybe_quote_one("a\nb\001", 0, "\"a\\nb\\001\"");
test_shell_maybe_quote_one("a\nb\001", SHELL_ESCAPE_POSIX, "$'a\\nb\\001'");
test_shell_maybe_quote_one("głąb", 0, "głąb");
test_shell_maybe_quote_one("głąb", SHELL_ESCAPE_POSIX, "głąb");
test_shell_maybe_quote_one("głąb\002\003", 0, "\"głąb\\002\\003\"");
test_shell_maybe_quote_one("głąb\002\003", SHELL_ESCAPE_POSIX, "$'głąb\\002\\003'");
test_shell_maybe_quote_one("głąb\002\003rząd", 0, "\"głąb\\002\\003rząd\"");
test_shell_maybe_quote_one("głąb\002\003rząd", SHELL_ESCAPE_POSIX, "$'głąb\\002\\003rząd'");
}
int main(int argc, char *argv[]) {
+50 -17
View File
@@ -322,6 +322,8 @@ static void test_executable_is_script(void) {
char *command;
int r;
log_info("/* %s */", __func__);
assert_se(fmkostemp_safe(t, "w", &f) == 0);
fputs("#! /bin/script -a -b \ngoo goo", f);
fflush(f);
@@ -347,6 +349,8 @@ static void test_status_field(void) {
unsigned long long total = 0, buffers = 0;
int r;
log_info("/* %s */", __func__);
assert_se(get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t) == 0);
puts(t);
assert_se(streq(t, "1"));
@@ -378,11 +382,11 @@ static void test_status_field(void) {
}
static void test_capeff(void) {
int pid, p;
log_info("/* %s */", __func__);
for (pid = 0; pid < 2; pid++) {
for (int pid = 0; pid < 2; pid++) {
_cleanup_free_ char *capeff = NULL;
int r;
int r, p;
r = get_process_capeff(0, &capeff);
log_info("capeff: '%s' (r=%d)", capeff, r);
@@ -403,6 +407,8 @@ static void test_write_string_stream(void) {
int fd;
char buf[64];
log_info("/* %s */", __func__);
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
@@ -437,6 +443,8 @@ static void test_write_string_file(void) {
char buf[64] = {};
_cleanup_close_ int fd;
log_info("/* %s */", __func__);
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
@@ -451,6 +459,8 @@ static void test_write_string_file_no_create(void) {
_cleanup_close_ int fd;
char buf[64] = {};
log_info("/* %s */", __func__);
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
@@ -465,6 +475,8 @@ static void test_write_string_file_verify(void) {
_cleanup_free_ char *buf = NULL, *buf2 = NULL;
int r;
log_info("/* %s */", __func__);
r = read_one_line_file("/proc/version", &buf);
if (ERRNO_IS_PRIVILEGE(r))
return;
@@ -491,6 +503,8 @@ static void test_load_env_file_pairs(void) {
_cleanup_strv_free_ char **l = NULL;
char **k, **v;
log_info("/* %s */", __func__);
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
@@ -538,6 +552,8 @@ static void test_search_and_fopen(void) {
const char *e;
int r;
log_info("/* %s */", __func__);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
fd = safe_close(fd);
@@ -579,6 +595,8 @@ static void test_search_and_fopen_nulstr(void) {
"/tmp/foo/bar\0"
"/tmp\0";
log_info("/* %s */", __func__);
_cleanup_(unlink_tempfilep) char name[] = "/tmp/test-search_and_fopen.XXXXXX";
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
@@ -620,12 +638,15 @@ static void test_writing_tmpfile(void) {
_cleanup_free_ char *contents = NULL;
size_t size;
_cleanup_close_ int fd = -1;
struct iovec iov[3];
int r;
iov[0] = IOVEC_MAKE_STRING("abc\n");
iov[1] = IOVEC_MAKE_STRING(ALPHANUMERICAL "\n");
iov[2] = IOVEC_MAKE_STRING("");
log_info("/* %s */", __func__);
struct iovec iov[] = {
IOVEC_MAKE_STRING("abc\n"),
IOVEC_MAKE_STRING(ALPHANUMERICAL "\n"),
IOVEC_MAKE_STRING(""),
};
fd = mkostemp_safe(name);
printf("tmpfile: %s", name);
@@ -642,6 +663,8 @@ static void test_writing_tmpfile(void) {
static void test_tempfn(void) {
char *ret = NULL, *p;
log_info("/* %s */", __func__);
assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL, &ret) >= 0);
assert_se(streq_ptr(ret, "/foo/bar/.#waldoXXXXXX"));
free(ret);
@@ -684,8 +707,7 @@ static void test_fgetc(void) {
_cleanup_fclose_ FILE *f = NULL;
char c;
f = fmemopen_unlocked((void*) chars, sizeof(chars), "re");
assert_se(f);
assert_se(f = fmemopen_unlocked((void*) chars, sizeof(chars), "re"));
for (size_t i = 0; i < sizeof(chars); i++) {
assert_se(safe_fgetc(f, &c) == 1);
@@ -778,9 +800,9 @@ static void test_read_line_one_file(FILE *f) {
static void test_read_line(void) {
_cleanup_fclose_ FILE *f = NULL;
f = fmemopen_unlocked((void*) buffer, sizeof(buffer), "re");
assert_se(f);
log_info("/* %s */", __func__);
assert_se(f = fmemopen_unlocked((void*) buffer, sizeof(buffer), "re"));
test_read_line_one_file(f);
}
@@ -789,6 +811,8 @@ static void test_read_line2(void) {
int fd;
_cleanup_fclose_ FILE *f = NULL;
log_info("/* %s */", __func__);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se((size_t) write(fd, buffer, sizeof(buffer)) == sizeof(buffer));
@@ -804,6 +828,8 @@ static void test_read_line3(void) {
_cleanup_free_ char *line = NULL;
int r;
log_info("/* %s */", __func__);
f = fopen("/proc/uptime", "re");
if (!f && IN_SET(errno, ENOENT, EPERM))
return;
@@ -836,10 +862,9 @@ static void test_read_line4(void) {
{ 6, "foo\n\r\0" },
};
size_t i;
int r;
for (i = 0; i < ELEMENTSOF(eof_endings); i++) {
for (size_t i = 0; i < ELEMENTSOF(eof_endings); i++) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *s = NULL;
@@ -864,6 +889,8 @@ static void test_read_nul_string(void) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *s = NULL;
log_info("/* %s */", __func__);
assert_se(f = fmemopen_unlocked((void*) test, sizeof(test)-1, "r"));
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 13 && streq_ptr(s, "string nr. 1"));
@@ -953,6 +980,8 @@ static void test_read_full_file_offset_size(void) {
size_t rbuf_size;
uint8_t buf[4711];
log_info("/* %s */", __func__);
random_bytes(buf, sizeof(buf));
assert_se(tempfn_random_child(NULL, NULL, &fn) >= 0);
@@ -990,10 +1019,12 @@ static void test_read_full_file_offset_size(void) {
rbuf = mfree(rbuf);
}
static void test_read_full_virtual_file(void) {
static void test_read_virtual_file(size_t max_size) {
const char *filename;
int r;
log_info("/* %s (max_size=%zu) */", __func__, max_size);
FOREACH_STRING(filename,
"/proc/1/cmdline",
"/etc/nsswitch.conf",
@@ -1002,8 +1033,8 @@ static void test_read_full_virtual_file(void) {
_cleanup_free_ char *buf = NULL;
size_t size = 0;
r = read_full_virtual_file(filename, &buf, &size);
log_info_errno(r, "read_full_virtual_file(\"%s\"): %m (%zu bytes)", filename, size);
r = read_virtual_file(filename, max_size, &buf, &size);
log_info_errno(r, "read_virtual_file(\"%s\", %zu): %m (%zu bytes)", filename, max_size, size);
assert_se(r == 0 || ERRNO_IS_PRIVILEGE(r) || r == -ENOENT);
}
}
@@ -1035,7 +1066,9 @@ int main(int argc, char *argv[]) {
test_read_nul_string();
test_read_full_file_socket();
test_read_full_file_offset_size();
test_read_full_virtual_file();
test_read_virtual_file(20);
test_read_virtual_file(4096);
test_read_virtual_file(SIZE_MAX);
return 0;
}
+201 -53
View File
@@ -15,6 +15,8 @@
#include "alloc-util.h"
#include "architecture.h"
#include "errno-util.h"
#include "errno-list.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "log.h"
#include "macro.h"
@@ -43,6 +45,8 @@ static void test_get_process_comm(pid_t pid) {
dev_t h;
int r;
log_info("/* %s */", __func__);
xsprintf(path, "/proc/"PID_FMT"/comm", pid);
if (stat(path, &st) == 0) {
@@ -88,15 +92,61 @@ static void test_get_process_comm(pid_t pid) {
log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
}
static void test_get_process_cmdline_one(pid_t pid) {
_cleanup_free_ char *c = NULL, *d = NULL, *e = NULL, *f = NULL, *g = NULL, *h = NULL;
int r;
r = get_process_cmdline(pid, SIZE_MAX, 0, &c);
log_info("PID "PID_FMT": %s", pid, r >= 0 ? c : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &d);
log_info(" %s", r >= 0 ? d : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &e);
log_info(" %s", r >= 0 ? e : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE | PROCESS_CMDLINE_COMM_FALLBACK, &f);
log_info(" %s", r >= 0 ? f : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &g);
log_info(" %s", r >= 0 ? g : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX | PROCESS_CMDLINE_COMM_FALLBACK, &h);
log_info(" %s", r >= 0 ? h : errno_to_name(r));
}
static void test_get_process_cmdline(void) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
log_info("/* %s */", __func__);
assert_se(d = opendir("/proc"));
FOREACH_DIRENT(de, d, return) {
pid_t pid;
dirent_ensure_type(d, de);
if (de->d_type != DT_DIR)
continue;
if (parse_pid(de->d_name, &pid) < 0)
continue;
test_get_process_cmdline_one(pid);
}
}
static void test_get_process_comm_escape_one(const char *input, const char *output) {
_cleanup_free_ char *n = NULL;
log_info("input: <%s> — output: <%s>", input, output);
log_debug("input: <%s> — output: <%s>", input, output);
assert_se(prctl(PR_SET_NAME, input) >= 0);
assert_se(get_process_comm(0, &n) >= 0);
log_info("got: <%s>", n);
log_debug("got: <%s>", n);
assert_se(streq_ptr(n, output));
}
@@ -104,6 +154,8 @@ static void test_get_process_comm_escape_one(const char *input, const char *outp
static void test_get_process_comm_escape(void) {
_cleanup_free_ char *saved = NULL;
log_info("/* %s */", __func__);
assert_se(get_process_comm(0, &saved) >= 0);
test_get_process_comm_escape_one("", "");
@@ -140,6 +192,8 @@ static void test_pid_is_unwaited(void) {
static void test_pid_is_alive(void) {
pid_t pid;
log_info("/* %s */", __func__);
pid = fork();
assert_se(pid >= 0);
if (pid == 0) {
@@ -155,6 +209,7 @@ static void test_pid_is_alive(void) {
}
static void test_personality(void) {
log_info("/* %s */", __func__);
assert_se(personality_to_string(PER_LINUX));
assert_se(!personality_to_string(PERSONALITY_INVALID));
@@ -183,6 +238,8 @@ static void test_get_process_cmdline_harder(void) {
_cleanup_free_ char *line = NULL;
pid_t pid;
log_info("/* %s */", __func__);
if (geteuid() != 0) {
log_info("Skipping %s: not root", __func__);
return;
@@ -245,151 +302,219 @@ static void test_get_process_cmdline_harder(void) {
assert_se(prctl(PR_SET_NAME, "testa") >= 0);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) == -ENOENT);
assert_se(get_process_cmdline(0, SIZE_MAX, 0, &line) == -ENOENT);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[testa]"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 0, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_info("'%s'", line);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_QUOTE, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "\"[testa]\"")); /* quoting is enabled here */
line = mfree(line);
assert_se(get_process_cmdline(0, 0, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, ""));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[t…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[te…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[tes…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[test…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[testa]"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[testa]"));
line = mfree(line);
/* Test with multiple arguments that don't require quoting */
assert_se(write(fd, "foo\0bar", 8) == 8);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) >= 0);
log_info("'%s'", line);
assert_se(get_process_cmdline(0, SIZE_MAX, 0, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "foo bar"));
line = mfree(line);
assert_se(write(fd, "quux", 4) == 4);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) >= 0);
log_info("'%s'", line);
assert_se(get_process_cmdline(0, SIZE_MAX, 0, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "f…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "fo…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo …"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo b…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo ba…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 9, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 9, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar …"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar q…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar qu…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 13, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 13, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 14, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 14, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(ftruncate(fd, 0) >= 0);
assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) == -ENOENT);
assert_se(get_process_cmdline(0, SIZE_MAX, 0, &line) == -ENOENT);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[aaaa bbbb cccc]"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[aaaa bbb…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[aaaa bbbb…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[aaaa bbbb …"));
line = mfree(line);
/* Test with multiple arguments that do require quoting */
#define CMDLINE1 "foo\0'bar'\0\"bar$\"\0x y z\0!``\0"
#define EXPECT1 "foo \"'bar'\" \"\\\"bar\\$\\\"\" \"x y z\" \"!\\`\\`\" \"\""
#define EXPECT1p "foo $'\\'bar\\'' $'\"bar$\"' $'x y z' $'!``' \"\""
assert_se(lseek(fd, SEEK_SET, 0) == 0);
assert_se(write(fd, CMDLINE1, sizeof CMDLINE1) == sizeof CMDLINE1);
assert_se(ftruncate(fd, sizeof CMDLINE1) == 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT1);
assert_se(streq(line, EXPECT1));
line = mfree(line);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT1p);
assert_se(streq(line, EXPECT1p));
line = mfree(line);
#define CMDLINE2 "foo\0\1\2\3\0\0"
#define EXPECT2 "foo \"\\001\\002\\003\" \"\" \"\""
#define EXPECT2p "foo $'\\001\\002\\003' \"\" \"\""
assert_se(lseek(fd, SEEK_SET, 0) == 0);
assert_se(write(fd, CMDLINE2, sizeof CMDLINE2) == sizeof CMDLINE2);
assert_se(ftruncate(fd, sizeof CMDLINE2) == 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT2);
assert_se(streq(line, EXPECT2));
line = mfree(line);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT2p);
assert_se(streq(line, EXPECT2p));
line = mfree(line);
safe_close(fd);
_exit(EXIT_SUCCESS);
}
@@ -398,11 +523,15 @@ static void test_rename_process_now(const char *p, int ret) {
_cleanup_free_ char *comm = NULL, *cmdline = NULL;
int r;
log_info("/* %s */", __func__);
r = rename_process(p);
assert_se(r == ret ||
(ret == 0 && r >= 0) ||
(ret > 0 && r > 0));
log_debug_errno(r, "rename_process(%s): %m", p);
if (r < 0)
return;
@@ -413,7 +542,7 @@ static void test_rename_process_now(const char *p, int ret) {
#endif
assert_se(get_process_comm(0, &comm) >= 0);
log_info("comm = <%s>", comm);
log_debug("comm = <%s>", comm);
assert_se(strneq(comm, p, TASK_COMM_LEN-1));
/* We expect comm to be at most 16 bytes (TASK_COMM_LEN). The kernel may raise this limit in the
* future. We'd only check the initial part, at least until we recompile, but this will still pass. */
@@ -425,9 +554,12 @@ static void test_rename_process_now(const char *p, int ret) {
if (r == 0 && detect_container() > 0)
log_info("cmdline = <%s> (not verified, Running in unprivileged container?)", cmdline);
else {
log_info("cmdline = <%s>", cmdline);
assert_se(strneq(p, cmdline, STRLEN("test-process-util")));
assert_se(startswith(p, cmdline));
log_info("cmdline = <%s> (expected <%.*s>)", cmdline, (int) strlen("test-process-util"), p);
bool skip = cmdline[0] == '"'; /* A shortcut to check if the string is quoted */
assert_se(strneq(cmdline + skip, p, strlen("test-process-util")));
assert_se(startswith(cmdline + skip, p));
}
} else
log_info("cmdline = <%s> (not verified)", cmdline);
@@ -437,6 +569,8 @@ static void test_rename_process_one(const char *p, int ret) {
siginfo_t si;
pid_t pid;
log_info("/* %s */", __func__);
pid = fork();
assert_se(pid >= 0);
@@ -491,6 +625,8 @@ static void test_getpid_cached(void) {
siginfo_t si;
pid_t a, b, c, d, e, f, child;
log_info("/* %s */", __func__);
a = raw_getpid();
b = getpid_cached();
c = getpid();
@@ -521,25 +657,28 @@ static void test_getpid_cached(void) {
assert_se(si.si_code == CLD_EXITED);
}
#define MEASURE_ITERATIONS (10000000LLU)
static void test_getpid_measure(void) {
unsigned long long i;
usec_t t, q;
unsigned long long iterations = slow_tests_enabled() ? 1000000 : 1000;
log_info("/* %s (%llu iterations) */", __func__, iterations);
t = now(CLOCK_MONOTONIC);
for (i = 0; i < MEASURE_ITERATIONS; i++)
for (unsigned long long i = 0; i < iterations; i++)
(void) getpid();
q = now(CLOCK_MONOTONIC) - t;
log_info(" glibc getpid(): %lf µs each\n", (double) q / MEASURE_ITERATIONS);
log_info(" glibc getpid(): %lf µs each\n", (double) q / iterations);
iterations *= 50; /* _cached() is about 50 times faster, so we need more iterations */
t = now(CLOCK_MONOTONIC);
for (i = 0; i < MEASURE_ITERATIONS; i++)
for (unsigned long long i = 0; i < iterations; i++)
(void) getpid_cached();
q = now(CLOCK_MONOTONIC) - t;
log_info("getpid_cached(): %lf µs each\n", (double) q / MEASURE_ITERATIONS);
log_info("getpid_cached(): %lf µs each\n", (double) q / iterations);
}
static void test_safe_fork(void) {
@@ -547,6 +686,8 @@ static void test_safe_fork(void) {
pid_t pid;
int r;
log_info("/* %s */", __func__);
BLOCK_SIGNALS(SIGCHLD);
r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid);
@@ -595,6 +736,8 @@ static void test_ioprio_class_from_to_string_one(const char *val, int expected)
}
static void test_ioprio_class_from_to_string(void) {
log_info("/* %s */", __func__);
test_ioprio_class_from_to_string_one("none", IOPRIO_CLASS_NONE);
test_ioprio_class_from_to_string_one("realtime", IOPRIO_CLASS_RT);
test_ioprio_class_from_to_string_one("best-effort", IOPRIO_CLASS_BE);
@@ -610,7 +753,10 @@ static void test_ioprio_class_from_to_string(void) {
static void test_setpriority_closest(void) {
int r;
r = safe_fork("(test-setprio)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL);
log_info("/* %s */", __func__);
r = safe_fork("(test-setprio)",
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL);
assert_se(r >= 0);
if (r == 0) {
@@ -694,7 +840,8 @@ static void test_setpriority_closest(void) {
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
log_show_color(true);
test_setup_logging(LOG_INFO);
save_argc_argv(argc, argv);
@@ -709,6 +856,7 @@ int main(int argc, char *argv[]) {
}
test_get_process_comm_escape();
test_get_process_cmdline();
test_pid_is_unwaited();
test_pid_is_alive();
test_personality();
+34 -33
View File
@@ -3,6 +3,7 @@
#include "alloc-util.h"
#include "string-util.h"
#include "strv.h"
#include "tests.h"
#include "utf8.h"
#include "util.h"
@@ -91,15 +92,15 @@ static void test_utf8_escape_invalid(void) {
log_info("/* %s */", __func__);
p1 = utf8_escape_invalid("goo goo goo");
puts(p1);
log_debug("\"%s\"", p1);
assert_se(utf8_is_valid(p1));
p2 = utf8_escape_invalid("\341\204\341\204");
puts(p2);
log_debug("\"%s\"", p2);
assert_se(utf8_is_valid(p2));
p3 = utf8_escape_invalid("\341\204");
puts(p3);
log_debug("\"%s\"", p3);
assert_se(utf8_is_valid(p3));
}
@@ -109,59 +110,56 @@ static void test_utf8_escape_non_printable(void) {
log_info("/* %s */", __func__);
p1 = utf8_escape_non_printable("goo goo goo");
puts(p1);
log_debug("\"%s\"", p1);
assert_se(utf8_is_valid(p1));
p2 = utf8_escape_non_printable("\341\204\341\204");
puts(p2);
log_debug("\"%s\"", p2);
assert_se(utf8_is_valid(p2));
p3 = utf8_escape_non_printable("\341\204");
puts(p3);
log_debug("\"%s\"", p3);
assert_se(utf8_is_valid(p3));
p4 = utf8_escape_non_printable("ąę\n가너도루\n1234\n\341\204\341\204\n\001 \019\20\a");
puts(p4);
log_debug("\"%s\"", p4);
assert_se(utf8_is_valid(p4));
p5 = utf8_escape_non_printable("\001 \019\20\a");
puts(p5);
log_debug("\"%s\"", p5);
assert_se(utf8_is_valid(p5));
p6 = utf8_escape_non_printable("\xef\xbf\x30\x13");
puts(p6);
log_debug("\"%s\"", p6);
assert_se(utf8_is_valid(p6));
}
static void test_utf8_escape_non_printable_full(void) {
log_info("/* %s */", __func__);
for (size_t i = 0; i < 20; i++) {
_cleanup_free_ char *p;
const char *s;
FOREACH_STRING(s,
"goo goo goo", /* ASCII */
"\001 \019\20\a", /* control characters */
"\xef\xbf\x30\x13") /* misplaced continuation bytes followed by a digit and cc */
for (size_t cw = 0; cw < 22; cw++) {
_cleanup_free_ char *p, *q;
size_t ew;
p = utf8_escape_non_printable_full("goo goo goo", i);
puts(p);
assert_se(utf8_is_valid(p));
assert_se(utf8_console_width(p) <= i);
}
p = utf8_escape_non_printable_full(s, cw, false);
ew = utf8_console_width(p);
log_debug("%02zu \"%s\" (%zu wasted)", cw, p, cw - ew);
assert_se(utf8_is_valid(p));
assert_se(ew <= cw);
for (size_t i = 0; i < 20; i++) {
_cleanup_free_ char *p;
p = utf8_escape_non_printable_full("\001 \019\20\a", i);
puts(p);
assert_se(utf8_is_valid(p));
assert_se(utf8_console_width(p) <= i);
}
for (size_t i = 0; i < 20; i++) {
_cleanup_free_ char *p;
p = utf8_escape_non_printable_full("\xef\xbf\x30\x13", i);
puts(p);
assert_se(utf8_is_valid(p));
assert_se(utf8_console_width(p) <= i);
}
q = utf8_escape_non_printable_full(s, cw, true);
ew = utf8_console_width(q);
log_debug(" \"%s\" (%zu wasted)", q, cw - ew);
assert_se(utf8_is_valid(q));
assert_se(ew <= cw);
if (cw > 0)
assert_se(endswith(q, "…"));
}
}
static void test_utf16_to_utf8(void) {
@@ -235,6 +233,9 @@ static void test_utf8_to_utf16(void) {
}
int main(int argc, char *argv[]) {
log_show_color(true);
test_setup_logging(LOG_INFO);
test_utf8_n_is_valid();
test_utf8_is_valid();
test_utf8_is_printable();