From: Stephen Hemminger Since the process name is under user control with prctl(PR_SET_NAME) it may contain escape characters to try and mess with screen output. Reuse the existing string logic from procps (used by ps command). Reported-by: Josiah Stearns Signed-off-by: Stephen Hemminger Reviewed-by: Michal Kubecek Tested-by: Michal Kubecek --- include/utils.h | 2 + lib/Makefile | 2 +- lib/escape.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++ misc/ss.c | 10 ++--- 4 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 lib/escape.c diff --git a/include/utils.h b/include/utils.h index adf6a581a2a6..8b7e6d9e2c3b 100644 --- a/include/utils.h +++ b/include/utils.h @@ -369,6 +369,8 @@ const char *str_map_lookup_u8(const struct str_num_map *map, uint8_t val); unsigned int get_str_char_count(const char *str, int match); int str_split_by_char(char *str, char **before, char **after, int match); +int escape_str (char *dst, const char *src, int bufsize); + #define INDENT_STR_MAXLEN 32 struct indent_mem { diff --git a/lib/Makefile b/lib/Makefile index cd561bc09235..7035f40f8953 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -6,7 +6,7 @@ CFLAGS += -fPIC UTILOBJ = utils.o utils_math.o rt_names.o ll_map.o ll_types.o ll_proto.o ll_addr.o \ inet_proto.o namespace.o json_writer.o json_print.o json_print_math.o \ names.o color.o bpf_legacy.o bpf_glue.o exec.o fs.o cg_map.o \ - ppp_proto.o bridge.o sha1.o + ppp_proto.o bridge.o sha1.o escape.o ifeq ($(HAVE_ELF),y) ifeq ($(HAVE_LIBBPF),y) diff --git a/lib/escape.c b/lib/escape.c new file mode 100644 index 000000000000..b110f61b3038 --- /dev/null +++ b/lib/escape.c @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Escape character print handling derived from procps + * Copyright 1998-2002 by Albert Cahalan + * Copyright 2020-2022 Jim Warner + * + */ + +#include +#include +#include +#include + +#include "utils.h" + +static const char UTF_tab[] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 - 0x0F + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 - 0x1F + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 - 0x2F + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 - 0x3F + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 - 0x4F + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 - 0x5F + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6F + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 - 0x7F + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 0x80 - 0x8F + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 0x90 - 0x9F + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 0xA0 - 0xAF + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 0xB0 - 0xBF + -1, -1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, // 0xC0 - 0xCF + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, // 0xD0 - 0xDF + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, // 0xE0 - 0xEF + 4, 4, 4, 4, 4, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 0xF0 - 0xFF +}; + +static const unsigned char ESC_tab[] = { + "@..............................." // 0x00 - 0x1F + "||||||||||||||||||||||||||||||||" // 0x20 - 0x3F + "||||||||||||||||||||||||||||||||" // 0x40 - 0x5f + "|||||||||||||||||||||||||||||||." // 0x60 - 0x7F + "????????????????????????????????" // 0x80 - 0x9F + "????????????????????????????????" // 0xA0 - 0xBF + "????????????????????????????????" // 0xC0 - 0xDF + "????????????????????????????????" // 0xE0 - 0xFF +}; + +static void esc_all(unsigned char *str) +{ + // if bad locale/corrupt str, replace non-printing stuff + while (*str) { + unsigned char c = ESC_tab[*str]; + + if (c != '|') + *str = c; + ++str; + } +} + +static void esc_ctl(unsigned char *str, int len) +{ + int i; + + for (i = 0; i < len;) { + // even with a proper locale, strings might be corrupt + int n = UTF_tab[*str]; + + if (n < 0 || i + n > len) { + esc_all(str); + return; + } + // and eliminate those non-printing control characters + if (*str < 0x20 || *str == 0x7f) + *str = '?'; + str += n; + i += n; + } +} + +int escape_str(char *dst, const char *src, int bufsize) +{ + static int utf_sw; + + if (utf_sw == 0) { + char *enc = nl_langinfo(CODESET); + + utf_sw = enc && strcasecmp(enc, "UTF-8") == 0 ? 1 : -1; + } + + int n = strlcpy(dst, src, bufsize); + + if (utf_sw < 0) + esc_all((unsigned char *)dst); + else + esc_ctl((unsigned char *)dst, n); + return n; +} diff --git a/misc/ss.c b/misc/ss.c index 28a82c669493..121ca6d55cb7 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -550,8 +550,7 @@ static void user_ent_add(unsigned int ino, char *task, static void user_ent_hash_build_task(char *path, int pid, int tid) { const char *no_ctx = "unavailable"; - char task[16] = {'\0', }; - char stat[MAX_PATH_LEN]; + char task[20] = { }; int pos_id, pos_fd; char *task_context; struct dirent *d; @@ -599,6 +598,8 @@ static void user_ent_hash_build_task(char *path, int pid, int tid) sock_context = strdup(no_ctx); if (task[0] == '\0') { + char stat[MAX_PATH_LEN]; + char name[16]; FILE *fp; strlcpy(stat, path, pos_id + 1); @@ -606,9 +607,8 @@ static void user_ent_hash_build_task(char *path, int pid, int tid) fp = fopen(stat, "r"); if (fp) { - if (fscanf(fp, "%*d (%[^)])", task) < 1) { - ; /* ignore */ - } + if (fscanf(fp, "%*d (%[^)])", name) == 1) + escape_str(task, name, sizeof(task)); fclose(fp); } } -- 2.53.0