]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
ss: escape characters in command name main
authorStephen Hemminger <stephen@networkplumber.org>
Wed, 25 Mar 2026 23:03:02 +0000 (00:03 +0100)
committerStephen Hemminger <stephen@networkplumber.org>
Sun, 5 Apr 2026 15:50:17 +0000 (08:50 -0700)
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 <B00TK1D@proton.me>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Reviewed-by: Michal Kubecek <mkubecek@suse.cz>
Tested-by: Michal Kubecek <mkubecek@suse.cz>
include/utils.h
lib/Makefile
lib/escape.c [new file with mode: 0644]
misc/ss.c

index adf6a581a2a6f6d56a2dadfcff290f3d233c91c7..b68d6bc4edcf9ca335148f731cd240871b6c809b 100644 (file)
@@ -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);
 
 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 {
 #define INDENT_STR_MAXLEN 32
 
 struct indent_mem {
index cd561bc09235bdc560330e1bcf350f5d2bbc55a1..7035f40f89530e2a64e18233d26c0f814146fe96 100644 (file)
@@ -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 \
 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)
 
 ifeq ($(HAVE_ELF),y)
 ifeq ($(HAVE_LIBBPF),y)
diff --git a/lib/escape.c b/lib/escape.c
new file mode 100644 (file)
index 0000000..b110f61
--- /dev/null
@@ -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 <james.warner@comcast.net>
+ *
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <langinfo.h>
+
+#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;
+}
index 28a82c669493695489670e2462c60a0f54f6b70c..121ca6d55cb7a1ec2ee333b69be72d6affa6178a 100644 (file)
--- 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";
 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;
        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') {
                        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);
                        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) {
 
                        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);
                        }
                }
                                fclose(fp);
                        }
                }