netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH iproute2] NULL Pointer Dereference vulnerability and patch
@ 2025-02-19  9:11 Ziao Li
  2025-02-25 18:19 ` Stephen Hemminger
  0 siblings, 1 reply; 2+ messages in thread
From: Ziao Li @ 2025-02-19  9:11 UTC (permalink / raw)
  To: netdev

[-- Attachment #1: Type: text/plain, Size: 4657 bytes --]

NULL Pointer Dereference vulnerability in iproute2.
The vulnerability happens in load_ugly_table(), misc/nstat.c, in the
latest version of iproute2 (41710ace5e8fadff354f3dba67bf27ed3a3c5ae7)
How the vulnerability happens:
1. db is set to NULL at struct nstat_ent *db = NULL;
2. n is set to NULL at n = db;
3. NULL dereference of variable n happens at sscanf(p+1, "%llu", &n->val) != 1
static void load_ugly_table(FILE *fp)
{
        char *buf = NULL;
        size_t buflen = 0;
        ssize_t nread;
        struct nstat_ent *db = NULL;
        struct nstat_ent *n;

        while ((nread = getline(&buf, &buflen, fp)) != -1) {
                char idbuf[4096];
                int  off;
                char *p;
                int count1, count2, skip = 0;

                p = strchr(buf, ':');
                if (!p) {
                        fprintf(stderr, "%s:%d: error parsing history file\n",
                                __FILE__, __LINE__);
                        exit(-2);
                }
                count1 = count_spaces(buf);
                *p = 0;
                idbuf[0] = 0;
                strncat(idbuf, buf, sizeof(idbuf) - 1);
                off = p - buf;
                p += 2;

                while (*p) {
                    ......
                }
                n = db;
                nread = getline(&buf, &buflen, fp);
                if (nread == -1) {
                        fprintf(stderr, "%s:%d: error parsing history file\n",
                                __FILE__, __LINE__);
                        exit(-2);
                }
                count2 = count_spaces(buf);
                if (count2 > count1)
                        skip = count2 - count1;
                do {
                        p = strrchr(buf, ' ');
                        if (!p) {
                                fprintf(stderr, "%s:%d: error parsing
history file\n",
                                        __FILE__, __LINE__);
                                exit(-2);
                        }
                        *p = 0;
                        if (sscanf(p+1, "%llu", &n->val) != 1) {
                                fprintf(stderr, "%s:%d: error parsing
history file\n",
                                        __FILE__, __LINE__);
                                exit(-2);
                        }
                        /* Trick to skip "dummy" trailing ICMP MIB in 2.4 */
                        if (skip)
                                skip--;
                        else
                                n = n->next;
                } while (p > buf + off + 2);
        }
        free(buf);
        ......
}

---
Steps to reproduce:
1. Put attachment files file at misc/poc.c and misc/crash.txt
2. Compile poc.c file with:
gcc -Wall -Wstrict-prototypes  -Wmissing-prototypes
-Wmissing-declarations -Wold-style-definition -Wformat=2 -g -O0 -pipe
-I../include -I../include/uapi -DRESOLVE_HOSTNAMES
-DLIBDIR=\"/usr/lib\" -DCONF_USR_DIR=\"/usr/share/iproute2\"
-DCONF_ETC_DIR=\"/etc/iproute2\" -DNETNS_RUN_DIR=\"/var/run/netns\"
-DNETNS_ETC_DIR=\"/etc/netns\" -DARPDDIR=\"/var/lib/arpd\"
-DCONF_COLOR=COLOR_OPT_NEVER -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -DHAVE_SETNS
-DHAVE_HANDLE_AT -DHAVE_SELINUX  -DHAVE_RPC -I/usr/include/tirpc
-DHAVE_ELF  -DNEED_STRLCPY -DHAVE_LIBCAP  -DHAVE_SETNS
-DHAVE_HANDLE_AT -DHAVE_SELINUX  -DHAVE_RPC -I/usr/include/tirpc
-DHAVE_ELF  -DNEED_STRLCPY -DHAVE_LIBCAP -o poc poc.c -lselinux
-ltirpc -lelf -lcap ../lib/libutil.a ../lib/libnetlink.a -lselinux
-ltirpc -lelf -lcap -lm
3. Run the poc by
$ ./poc crash.txt
zsh: segmentation fault (core dumped)  ./poc crash.txt
---
Patch for the vulnerability:

From 2f462d5adf071827285291d2ce13119e220681fd Mon Sep 17 00:00:00 2001
From: lza <leeziao0331@gmail.com>
Date: Wed, 19 Feb 2025 08:38:48 +0000
Subject: [PATCH] Fix Null Dereference when no entries are specified

---
 misc/nstat.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/misc/nstat.c b/misc/nstat.c
index fce3e9c1..b2e19bde 100644
--- a/misc/nstat.c
+++ b/misc/nstat.c
@@ -218,6 +218,10 @@ static void load_ugly_table(FILE *fp)
            p = next;
        }
        n = db;
+       if (n == NULL) {
+           fprintf(stderr, "Error: Invalid input – line has ':' but
no entries. Add values after ':'.\n");
+           exit(-2);
+       }
        nread = getline(&buf, &buflen, fp);
        if (nread == -1) {
            fprintf(stderr, "%s:%d: error parsing history file\n",
-- 
2.34.1

[-- Attachment #2: crash.txt --]
[-- Type: text/plain, Size: 10 bytes --]

test:
 123

[-- Attachment #3: poc.c --]
[-- Type: application/octet-stream, Size: 11613 bytes --]

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * nstat.c	handy utility to read counters /proc/net/netstat and snmp
 *
 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 */

 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
 #include <errno.h>
 #include <time.h>
 #include <sys/time.h>
 #include <fnmatch.h>
 #include <sys/file.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <poll.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <signal.h>
 #include <math.h>
 #include <getopt.h>
 
 #include <json_writer.h>
 #include "version.h"
 #include "utils.h"
 
 int dump_zeros;
 int reset_history;
 int ignore_history;
 int no_output;
 int json_output;
 int no_update;
 int scan_interval;
 int time_constant;
 double W;
 char **patterns;
 int npatterns;
 
 char info_source[128];
 int source_mismatch;
 
 static FILE *net_netstat_open(void)
 {
	 return generic_proc_open("PROC_NET_NETSTAT", "net/netstat");
 }
 
 static FILE *net_snmp_open(void)
 {
	 return generic_proc_open("PROC_NET_SNMP", "net/snmp");
 }
 
 static FILE *net_snmp6_open(void)
 {
	 return generic_proc_open("PROC_NET_SNMP6", "net/snmp6");
 }
 
 static FILE *net_sctp_snmp_open(void)
 {
	 return generic_proc_open("PROC_NET_SCTP_SNMP", "net/sctp/snmp");
 }
 
 struct nstat_ent {
	 struct nstat_ent *next;
	 char		 *id;
	 unsigned long long val;
	 double		   rate;
 };
 
 struct nstat_ent *kern_db;
 struct nstat_ent *hist_db;
 
 static const char *useless_numbers[] = {
	 "IpForwarding", "IpDefaultTTL",
	 "TcpRtoAlgorithm", "TcpRtoMin", "TcpRtoMax",
	 "TcpMaxConn", "TcpCurrEstab"
 };
 
 static int useless_number(const char *id)
 {
	 int i;
 
	 for (i = 0; i < ARRAY_SIZE(useless_numbers); i++)
		 if (strcmp(id, useless_numbers[i]) == 0)
			 return 1;
	 return 0;
 }
 
 static int match(const char *id)
 {
	 int i;
 
	 if (npatterns == 0)
		 return 1;
 
	 for (i = 0; i < npatterns; i++) {
		 if (!fnmatch(patterns[i], id, FNM_CASEFOLD))
			 return 1;
	 }
	 return 0;
 }
 
 static void load_good_table(FILE *fp)
 {
	 char buf[4096];
	 struct nstat_ent *db = NULL;
	 struct nstat_ent *n;
 
	 while (fgets(buf, sizeof(buf), fp) != NULL) {
		 int nr;
		 unsigned long long val;
		 double rate;
		 char idbuf[sizeof(buf)];
 
		 if (buf[0] == '#') {
			 buf[strlen(buf)-1] = 0;
			 if (info_source[0] && strcmp(info_source, buf+1))
				 source_mismatch = 1;
			 strlcpy(info_source, buf + 1, sizeof(info_source));
			 continue;
		 }
		 /* idbuf is as big as buf, so this is safe */
		 nr = sscanf(buf, "%s%llu%lg", idbuf, &val, &rate);
		 if (nr < 2) {
			 fprintf(stderr, "%s:%d: error parsing history file\n",
				 __FILE__, __LINE__);
			 exit(-2);
		 }
		 if (nr < 3)
			 rate = 0;
		 if (useless_number(idbuf))
			 continue;
		 if ((n = malloc(sizeof(*n))) == NULL) {
			 perror("nstat: malloc");
			 exit(-1);
		 }
		 n->id = strdup(idbuf);
		 n->val = val;
		 n->rate = rate;
		 n->next = db;
		 db = n;
	 }
 
	 while (db) {
		 n = db;
		 db = db->next;
		 n->next = kern_db;
		 kern_db = n;
	 }
 }
 
 static int count_spaces(const char *line)
 {
	 int count = 0;
	 char c;
 
	 while ((c = *line++) != 0)
		 count += c == ' ' || c == '\n';
	 return count;
 }
 
 static void load_ugly_table(FILE *fp)
 {
	 char *buf = NULL;
	 size_t buflen = 0;
	 ssize_t nread;
	 struct nstat_ent *db = NULL;
	 struct nstat_ent *n;
 
	 while ((nread = getline(&buf, &buflen, fp)) != -1) {
		 char idbuf[4096];
		 int  off;
		 char *p;
		 int count1, count2, skip = 0;
 
		 p = strchr(buf, ':');
		 if (!p) {
			 fprintf(stderr, "%s:%d: error parsing history file\n",
				 __FILE__, __LINE__);
			 exit(-2);
		 }
		 count1 = count_spaces(buf);
		 *p = 0;
		 idbuf[0] = 0;
		 strncat(idbuf, buf, sizeof(idbuf) - 1);
		 off = p - buf;
		 p += 2;
 
		 while (*p) {
			 char *next;
 
			 if ((next = strchr(p, ' ')) != NULL)
				 *next++ = 0;
			 else if ((next = strchr(p, '\n')) != NULL)
				 *next++ = 0;
			 if (off < sizeof(idbuf)) {
				 idbuf[off] = 0;
				 strncat(idbuf, p, sizeof(idbuf) - off - 1);
			 }
			 n = malloc(sizeof(*n));
			 if (!n) {
				 perror("nstat: malloc");
				 exit(-1);
			 }
			 n->id = strdup(idbuf);
			 if (n->id == NULL) {
				 perror("nstat: strdup");
				 exit(-1);
			 }
			 n->rate = 0;
			 n->next = db;
			 db = n;
			 if (next == NULL)
				 break;
			 p = next;
		 }
		 n = db;
		 nread = getline(&buf, &buflen, fp);
		 if (nread == -1) {
			 fprintf(stderr, "%s:%d: error parsing history file\n",
				 __FILE__, __LINE__);
			 exit(-2);
		 }
		 count2 = count_spaces(buf);
		 if (count2 > count1)
			 skip = count2 - count1;
		 do {
			 p = strrchr(buf, ' ');
			 if (!p) {
				 fprintf(stderr, "%s:%d: error parsing history file\n",
					 __FILE__, __LINE__);
				 exit(-2);
			 }
			 *p = 0;
			 if (sscanf(p+1, "%llu", &n->val) != 1) {
				 fprintf(stderr, "%s:%d: error parsing history file\n",
					 __FILE__, __LINE__);
				 exit(-2);
			 }
			 /* Trick to skip "dummy" trailing ICMP MIB in 2.4 */
			 if (skip)
				 skip--;
			 else
				 n = n->next;
		 } while (p > buf + off + 2);
	 }
	 free(buf);
 
	 while (db) {
		 n = db;
		 db = db->next;
		 if (useless_number(n->id)) {
			 free(n->id);
			 free(n);
		 } else {
			 n->next = kern_db;
			 kern_db = n;
		 }
	 }
 }
 
 static void load_sctp_snmp(void)
 {
	 FILE *fp = net_sctp_snmp_open();
 
	 if (fp) {
		 load_good_table(fp);
		 fclose(fp);
	 }
 }
 
 static void load_snmp(void)
 {
	 FILE *fp = net_snmp_open();
 
	 if (fp) {
		 load_ugly_table(fp);
		 fclose(fp);
	 }
 }
 
 static void load_snmp6(void)
 {
	 FILE *fp = net_snmp6_open();
 
	 if (fp) {
		 load_good_table(fp);
		 fclose(fp);
	 }
 }
 
 static void load_netstat(void)
 {
	 FILE *fp = net_netstat_open();
 
	 if (fp) {
		 load_ugly_table(fp);
		 fclose(fp);
	 }
 }
 
 
 static void dump_kern_db(FILE *fp, int to_hist)
 {
	 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
	 struct nstat_ent *n, *h;
 
	 h = hist_db;
	 if (jw) {
		 jsonw_start_object(jw);
		 jsonw_pretty(jw, pretty);
		 jsonw_name(jw, info_source);
		 jsonw_start_object(jw);
	 } else
		 fprintf(fp, "#%s\n", info_source);
 
	 for (n = kern_db; n; n = n->next) {
		 unsigned long long val = n->val;
 
		 if (!dump_zeros && !val && !n->rate)
			 continue;
		 if (!match(n->id)) {
			 struct nstat_ent *h1;
 
			 if (!to_hist)
				 continue;
			 for (h1 = h; h1; h1 = h1->next) {
				 if (strcmp(h1->id, n->id) == 0) {
					 val = h1->val;
					 h = h1->next;
					 break;
				 }
			 }
		 }
 
		 if (jw)
			 jsonw_uint_field(jw, n->id, val);
		 else
			 fprintf(fp, "%-32s%-16llu%6.1f\n", n->id, val, n->rate);
	 }
 
	 if (jw) {
		 jsonw_end_object(jw);
 
		 jsonw_end_object(jw);
		 jsonw_destroy(&jw);
	 }
 }
 
 static void dump_incr_db(FILE *fp)
 {
	 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
	 struct nstat_ent *n, *h;
 
	 h = hist_db;
	 if (jw) {
		 jsonw_start_object(jw);
		 jsonw_pretty(jw, pretty);
		 jsonw_name(jw, info_source);
		 jsonw_start_object(jw);
	 } else
		 fprintf(fp, "#%s\n", info_source);
 
	 for (n = kern_db; n; n = n->next) {
		 int ovfl = 0;
		 unsigned long long val = n->val;
		 struct nstat_ent *h1;
 
		 for (h1 = h; h1; h1 = h1->next) {
			 if (strcmp(h1->id, n->id) == 0) {
				 if (val < h1->val) {
					 ovfl = 1;
					 val = h1->val;
				 }
				 val -= h1->val;
				 h = h1->next;
				 break;
			 }
		 }
		 if (!dump_zeros && !val && !n->rate)
			 continue;
		 if (!match(n->id))
			 continue;
 
		 if (jw)
			 jsonw_uint_field(jw, n->id, val);
		 else
			 fprintf(fp, "%-32s%-16llu%6.1f%s\n", n->id, val,
				 n->rate, ovfl?" (overflow)":"");
	 }
 
	 if (jw) {
		 jsonw_end_object(jw);
 
		 jsonw_end_object(jw);
		 jsonw_destroy(&jw);
	 }
 }
 
 static int children;
 
 static void sigchild(int signo)
 {
 }
 
 static void update_db(int interval)
 {
	 struct nstat_ent *n, *h;
 
	 n = kern_db;
	 kern_db = NULL;
 
	 load_netstat();
	 load_snmp6();
	 load_snmp();
	 load_sctp_snmp();
 
	 h = kern_db;
	 kern_db = n;
 
	 for (n = kern_db; n; n = n->next) {
		 struct nstat_ent *h1;
 
		 for (h1 = h; h1; h1 = h1->next) {
			 if (strcmp(h1->id, n->id) == 0) {
				 double sample;
				 unsigned long long incr = h1->val - n->val;
 
				 n->val = h1->val;
				 sample = (double)incr * 1000.0 / interval;
				 if (interval >= scan_interval) {
					 n->rate += W*(sample-n->rate);
				 } else if (interval >= 1000) {
					 if (interval >= time_constant) {
						 n->rate = sample;
					 } else {
						 double w = W*(double)interval/scan_interval;
 
						 n->rate += w*(sample-n->rate);
					 }
				 }
 
				 while (h != h1) {
					 struct nstat_ent *tmp = h;
 
					 h = h->next;
					 free(tmp->id);
					 free(tmp);
				 };
				 h = h1->next;
				 free(h1->id);
				 free(h1);
				 break;
			 }
		 }
	 }
 }
 
 #define T_DIFF(a, b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000)
 
 
 static void server_loop(int fd)
 {
	 struct timeval snaptime = { 0 };
	 struct pollfd p;
 
	 p.fd = fd;
	 p.events = p.revents = POLLIN;
 
	 snprintf(info_source, sizeof(info_source), "%d.%lu sampling_interval=%d time_const=%d",
		 getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000);
 
	 load_netstat();
	 load_snmp6();
	 load_snmp();
	 load_sctp_snmp();
 
	 for (;;) {
		 int status;
		 time_t tdiff;
		 struct timeval now;
 
		 gettimeofday(&now, NULL);
		 tdiff = T_DIFF(now, snaptime);
		 if (tdiff >= scan_interval) {
			 update_db(tdiff);
			 snaptime = now;
			 tdiff = 0;
		 }
		 if (poll(&p, 1, scan_interval - tdiff) > 0
			 && (p.revents&POLLIN)) {
			 int clnt = accept(fd, NULL, NULL);
 
			 if (clnt >= 0) {
				 pid_t pid;
 
				 if (children >= 5) {
					 close(clnt);
				 } else if ((pid = fork()) != 0) {
					 if (pid > 0)
						 children++;
					 close(clnt);
				 } else {
					 FILE *fp = fdopen(clnt, "w");
 
					 if (fp)
						 dump_kern_db(fp, 0);
					 exit(0);
				 }
			 }
		 }
		 while (children && waitpid(-1, &status, WNOHANG) > 0)
			 children--;
	 }
 }
 
 static int verify_forging(int fd)
 {
	 struct ucred cred;
	 socklen_t olen = sizeof(cred);
 
	 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *)&cred, &olen) ||
		 olen < sizeof(cred))
		 return -1;
	 if (cred.uid == getuid() || cred.uid == 0)
		 return 0;
	 return -1;
 }
 
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
	 fprintf(stderr,
		 "Usage: nstat [OPTION] [ PATTERN [ PATTERN ] ]\n"
		 "   -h, --help          this message\n"
		 "   -a, --ignore        ignore history\n"
		 "   -d, --scan=SECS     sample every statistics every SECS\n"
		 "   -j, --json          format output in JSON\n"
		 "   -n, --nooutput      do history only\n"
		 "   -p, --pretty        pretty print\n"
		 "   -r, --reset         reset history\n"
		 "   -s, --noupdate      don't update history\n"
		 "   -t, --interval=SECS report average over the last SECS\n"
		 "   -V, --version       output version information\n"
		 "   -z, --zeros         show entries with zero activity\n");
	 exit(-1);
 }
 
 static const struct option longopts[] = {
	 { "help", 0, 0, 'h' },
	 { "ignore",  0,  0, 'a' },
	 { "scan", 1, 0, 'd'},
	 { "nooutput", 0, 0, 'n' },
	 { "json", 0, 0, 'j' },
	 { "reset", 0, 0, 'r' },
	 { "noupdate", 0, 0, 's' },
	 { "pretty", 0, 0, 'p' },
	 { "interval", 1, 0, 't' },
	 { "version", 0, 0, 'V' },
	 { "zeros", 0, 0, 'z' },
	 { 0 }
 };
 
 int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <input_file>\n", argv[0]);
        return -1;
    }
    FILE *fp = fopen(argv[1], "r");
    if (!fp) {
        perror("fopen");
        return -1;
    }
    load_ugly_table(fp);
    fclose(fp);
    return 0;
}

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH iproute2] NULL Pointer Dereference vulnerability and patch
  2025-02-19  9:11 [PATCH iproute2] NULL Pointer Dereference vulnerability and patch Ziao Li
@ 2025-02-25 18:19 ` Stephen Hemminger
  0 siblings, 0 replies; 2+ messages in thread
From: Stephen Hemminger @ 2025-02-25 18:19 UTC (permalink / raw)
  To: Ziao Li; +Cc: netdev

On Wed, 19 Feb 2025 17:11:51 +0800
Ziao Li <leeziao0331@gmail.com> wrote:

> NULL Pointer Dereference vulnerability in iproute2.
> The vulnerability happens in load_ugly_table(), misc/nstat.c, in the
> latest version of iproute2 (41710ace5e8fadff354f3dba67bf27ed3a3c5ae7)
> How the vulnerability happens:
> 1. db is set to NULL at struct nstat_ent *db = NULL;
> 2. n is set to NULL at n = db;
> 3. NULL dereference of variable n happens at sscanf(p+1, "%llu", &n->val) != 1
> static void load_ugly_table(FILE *fp)
> {
>         char *buf = NULL;
>         size_t buflen = 0;
>         ssize_t nread;
>         struct nstat_ent *db = NULL;
>         struct nstat_ent *n;
> 
>         while ((nread = getline(&buf, &buflen, fp)) != -1) {
>                 char idbuf[4096];
>                 int  off;
>                 char *p;
>                 int count1, count2, skip = 0;
> 
>                 p = strchr(buf, ':');
>                 if (!p) {
>                         fprintf(stderr, "%s:%d: error parsing history file\n",
>                                 __FILE__, __LINE__);
>                         exit(-2);
>                 }
>                 count1 = count_spaces(buf);
>                 *p = 0;
>                 idbuf[0] = 0;
>                 strncat(idbuf, buf, sizeof(idbuf) - 1);
>                 off = p - buf;
>                 p += 2;
> 
>                 while (*p) {
>                     ......
>                 }
>                 n = db;
>                 nread = getline(&buf, &buflen, fp);
>                 if (nread == -1) {
>                         fprintf(stderr, "%s:%d: error parsing history file\n",
>                                 __FILE__, __LINE__);
>                         exit(-2);
>                 }
>                 count2 = count_spaces(buf);
>                 if (count2 > count1)
>                         skip = count2 - count1;
>                 do {
>                         p = strrchr(buf, ' ');
>                         if (!p) {
>                                 fprintf(stderr, "%s:%d: error parsing
> history file\n",
>                                         __FILE__, __LINE__);
>                                 exit(-2);
>                         }
>                         *p = 0;
>                         if (sscanf(p+1, "%llu", &n->val) != 1) {
>                                 fprintf(stderr, "%s:%d: error parsing
> history file\n",
>                                         __FILE__, __LINE__);
>                                 exit(-2);
>                         }
>                         /* Trick to skip "dummy" trailing ICMP MIB in 2.4 */
>                         if (skip)
>                                 skip--;
>                         else
>                                 n = n->next;
>                 } while (p > buf + off + 2);
>         }
>         free(buf);
>         ......
> }
> 
> ---
> Steps to reproduce:
> 1. Put attachment files file at misc/poc.c and misc/crash.txt
> 2. Compile poc.c file with:
> gcc -Wall -Wstrict-prototypes  -Wmissing-prototypes
> -Wmissing-declarations -Wold-style-definition -Wformat=2 -g -O0 -pipe
> -I../include -I../include/uapi -DRESOLVE_HOSTNAMES
> -DLIBDIR=\"/usr/lib\" -DCONF_USR_DIR=\"/usr/share/iproute2\"
> -DCONF_ETC_DIR=\"/etc/iproute2\" -DNETNS_RUN_DIR=\"/var/run/netns\"
> -DNETNS_ETC_DIR=\"/etc/netns\" -DARPDDIR=\"/var/lib/arpd\"
> -DCONF_COLOR=COLOR_OPT_NEVER -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
> -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -DHAVE_SETNS
> -DHAVE_HANDLE_AT -DHAVE_SELINUX  -DHAVE_RPC -I/usr/include/tirpc
> -DHAVE_ELF  -DNEED_STRLCPY -DHAVE_LIBCAP  -DHAVE_SETNS
> -DHAVE_HANDLE_AT -DHAVE_SELINUX  -DHAVE_RPC -I/usr/include/tirpc
> -DHAVE_ELF  -DNEED_STRLCPY -DHAVE_LIBCAP -o poc poc.c -lselinux
> -ltirpc -lelf -lcap ../lib/libutil.a ../lib/libnetlink.a -lselinux
> -ltirpc -lelf -lcap -lm
> 3. Run the poc by
> $ ./poc crash.txt
> zsh: segmentation fault (core dumped)  ./poc crash.txt
> ---
> Patch for the vulnerability:
> 
> From 2f462d5adf071827285291d2ce13119e220681fd Mon Sep 17 00:00:00 2001
> From: lza <leeziao0331@gmail.com>
> Date: Wed, 19 Feb 2025 08:38:48 +0000
> Subject: [PATCH] Fix Null Dereference when no entries are specified
> 
> ---
>  misc/nstat.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/misc/nstat.c b/misc/nstat.c
> index fce3e9c1..b2e19bde 100644
> --- a/misc/nstat.c
> +++ b/misc/nstat.c
> @@ -218,6 +218,10 @@ static void load_ugly_table(FILE *fp)
>             p = next;
>         }
>         n = db;
> +       if (n == NULL) {
> +           fprintf(stderr, "Error: Invalid input – line has ':' but
> no entries. Add values after ':'.\n");
> +           exit(-2);
> +       }
>         nread = getline(&buf, &buflen, fp);
>         if (nread == -1) {
>             fprintf(stderr, "%s:%d: error parsing history file\n",


Thank you for finding and reporting a fix for a bug.
But this is not a vulnerability, just a bug.
And the patch is not formatted properly (line wrap) and
required Signed-off-by is missing.

Please fix and resubmit.



^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-02-25 18:19 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-19  9:11 [PATCH iproute2] NULL Pointer Dereference vulnerability and patch Ziao Li
2025-02-25 18:19 ` Stephen Hemminger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).