atop: patch CVE-2025-31160

Details: https://nvd.nist.gov/vuln/detail/CVE-2025-31160

Backport the patch that's subject references the CVE id explicitly.

I was able to verify the patch with a reproducer[1] (which is mentioned
in a reference[2] in the nvd report). Without the patch atop crashed,
with the patch it worked fine (both with and without -k/-K flags).

[1]: https://blog.bismuth.sh/blog/bismuth-found-the-atop-bug
[2]: https://gist.github.com/kallsyms/3acdf857ccc5c9fbaae7ed823be0365e

Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Gyorgy Sarvari 2025-12-29 21:54:12 +01:00 committed by Anuj Mittal
parent 02dbaa8843
commit 2b26d30fc7
No known key found for this signature in database
GPG Key ID: 4340AEFE69F5085C
2 changed files with 608 additions and 0 deletions

View File

@ -0,0 +1,607 @@
From 6427a26127cfec800b7064fd8700837c9f0c3548 Mon Sep 17 00:00:00 2001
From: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
Date: Sat, 29 Mar 2025 18:56:44 +0100
Subject: [PATCH] Fix security vulnerability CVE-2025-31160 (#334)
Atop will not connect to the TCP port of 'atopgpud' daemon any more
by default. The flag -k can be used explicitly when 'atopgpud' is
active. Also the code to parse the received strings is improved to
avoid future issues with heap corruption.
The flag -K has been implemented to connect to netatop/netatop-bpf.
CVE: CVE-2025-31160
Upstream-Status: Backport [https://github.com/Atoptool/atop/commit/ec8f3038497fcf493c6ff9c9f98f7a7c6216a1cb]
Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
---
atop.c | 59 +++++++--------
atop.h | 1 +
gpucom.c | 210 +++++++++++++++++++++++++++++++++++++---------------
photoproc.c | 3 +-
4 files changed, 182 insertions(+), 91 deletions(-)
diff --git a/atop.c b/atop.c
index 7ea9cc8..967df46 100644
--- a/atop.c
+++ b/atop.c
@@ -333,6 +333,8 @@ int ossub;
int supportflags; /* supported features */
char **argvp;
+char connectgpud = 0; /* boolean: connect to atopgpud */
+char connectnetatop = 0; /* boolean: connect to netatop(bpf) */
struct visualize vis = {generic_samp, generic_error,
generic_end, generic_usage};
@@ -573,7 +575,12 @@ main(int argc, char *argv[])
linelen = atoi(optarg);
break;
-
+ case 'k': /* try to open TCP connection to atopgpud */
+ connectgpud = 1;
+ break;
+ case 'K': /* try to open connection to netatop/netatop-bpf */
+ connectnetatop = 1;
+ break;
default: /* gather other flags */
flaglist[i++] = c;
}
@@ -688,7 +695,8 @@ main(int argc, char *argv[])
/*
** open socket to the IP layer to issue getsockopt() calls later on
*/
- netatop_ipopen();
+ if (connectnetatop)
+ netatop_ipopen();
/*
** since privileged activities are finished now, there is no
@@ -791,11 +799,15 @@ engine(void)
/*
** open socket to the atopgpud daemon for GPU statistics
+ ** if explicitly required
*/
- nrgpus = gpud_init();
+ if (connectgpud)
+ {
+ nrgpus = gpud_init();
- if (nrgpus)
- supportflags |= GPUSTAT;
+ if (nrgpus)
+ supportflags |= GPUSTAT;
+ }
/*
** MAIN-LOOP:
@@ -842,7 +854,10 @@ engine(void)
** send request for statistics to atopgpud
*/
if (nrgpus)
- gpupending = gpud_statrequest();
+ {
+ if ((gpupending = gpud_statrequest()) == 0)
+ nrgpus = 0;
+ }
/*
** take a snapshot of the current system-level statistics
@@ -867,28 +882,8 @@ engine(void)
// connection lost or timeout on receive?
if (nrgpuproc == -1)
{
- int ng;
-
- // try to reconnect
- ng = gpud_init();
-
- if (ng != nrgpus) // no success
- nrgpus = 0;
-
- if (nrgpus)
- {
- // request for stats again
- if (gpud_statrequest())
- {
- // receive stats response
- nrgpuproc = gpud_statresponse(nrgpus,
- cursstat->gpu.gpu, &gp);
-
- // persistent failure?
- if (nrgpuproc == -1)
- nrgpus = 0;
- }
- }
+ nrgpus = 0;
+ supportflags &= ~GPUSTAT;
}
cursstat->gpu.nrgpus = nrgpus;
@@ -977,7 +972,7 @@ engine(void)
/*
** merge GPU per-process stats with other per-process stats
*/
- if (nrgpus && nrgpuproc)
+ if (nrgpus && nrgpuproc > 0)
gpumergeproc(curtpres, ntaskpres,
curpexit, nprocexit,
gp, nrgpuproc);
@@ -1008,8 +1003,8 @@ engine(void)
if (nprocexitnet > 0)
netatop_exiterase();
- if (gp)
- free(gp);
+ free(gp);
+ gp = NULL; // avoid double free
if (lastcmd == 'r') /* reset requested ? */
{
@@ -1050,6 +1045,8 @@ prusage(char *myname)
printf("\t -P generate parseable output for specified label(s)\n");
printf("\t -L alternate line length (default 80) in case of "
"non-screen output\n");
+ printf("\t -k try to connect to external atopgpud daemon (default: do not connect)\n");
+ printf("\t -K try to connect to netatop/netatop-bpf interface (default: do not connect)\n");
(*vis.show_usage)();
diff --git a/atop.h b/atop.h
index 791c450..cbf4225 100644
--- a/atop.h
+++ b/atop.h
@@ -82,6 +82,7 @@ extern char threadview;
extern char calcpss;
extern char rawname[];
extern char rawreadflag;
+extern char connectnetatop;
extern unsigned int begintime, endtime;
extern char flaglist[];
extern struct visualize vis;
diff --git a/gpucom.c b/gpucom.c
index 4834851..690937e 100644
--- a/gpucom.c
+++ b/gpucom.c
@@ -43,12 +43,12 @@
#define GPUDPORT 59123
-static void gputype_parse(char *);
+static int gputype_parse(char *);
-static void gpustat_parse(int, char *, int,
+static int gpustat_parse(int, char *, int,
struct pergpu *, struct gpupidstat *);
-static void gpuparse(int, char *, struct pergpu *);
-static void pidparse(int, char *, struct gpupidstat *);
+static int gpuparse(int, char *, struct pergpu *);
+static int pidparse(int, char *, struct gpupidstat *);
static int rcvuntil(int, char *, int);
static int actsock = -1;
@@ -150,20 +150,24 @@ gpud_init(void)
if ( rcvuntil(actsock, buf, length) == -1)
{
perror("receive type request from atopgpud");
+ free(buf);
goto close_and_return;
}
buf[length] = '\0';
- gputype_parse(buf);
-
- numgpus = numgpus <= MAXGPU ? numgpus : MAXGPU;
+ if (! gputype_parse(buf))
+ {
+ free(buf);
+ goto close_and_return;
+ }
return numgpus;
close_and_return:
close(actsock);
actsock = -1;
+ numgpus = 0;
return 0;
}
@@ -176,7 +180,7 @@ gpud_init(void)
**
** Return value:
** 0 in case of failure
-** 1 in case of success
+** 1 in case of success (request pending)
*/
int
gpud_statrequest(void)
@@ -190,6 +194,7 @@ gpud_statrequest(void)
{
close(actsock);
actsock = -1;
+ numgpus = 0;
return 0;
}
@@ -216,7 +221,7 @@ gpud_statresponse(int maxgpu, struct pergpu *ggs, struct gpupidstat **gps)
uint32_t prelude;
char *buf = NULL, *p;
int version, length;
- int pids = 0;
+ int maxprocs = 0, nrprocs;
if (actsock == -1)
return -1;
@@ -269,22 +274,22 @@ gpud_statresponse(int maxgpu, struct pergpu *ggs, struct gpupidstat **gps)
*(buf+length) = '\0';
/*
- ** determine number of per-process stats
- ** and malloc space to parse these stats
+ ** determine number of per-process stats in string
+ ** and malloc space to store these stats
*/
for (p=buf; *p; p++)
{
if (*p == PIDDELIM)
- pids++;
+ maxprocs++;
}
if (gps)
{
- if (pids)
+ if (maxprocs)
{
- *gps = malloc(pids * sizeof(struct gpupidstat));
- ptrverify(gps, "Malloc failed for gpu pidstats\n");
- memset(*gps, 0, pids * sizeof(struct gpupidstat));
+ *gps = malloc(maxprocs * sizeof(struct gpupidstat));
+ ptrverify(*gps, "Malloc failed for gpu pidstats\n");
+ memset(*gps, 0, maxprocs * sizeof(struct gpupidstat));
}
else
{
@@ -295,18 +300,27 @@ gpud_statresponse(int maxgpu, struct pergpu *ggs, struct gpupidstat **gps)
/*
** parse stats string for per-gpu stats
*/
- gpustat_parse(version, buf, maxgpu, ggs, gps ? *gps : NULL);
+ if ( (nrprocs = gpustat_parse(version, buf, maxgpu, ggs, gps ? *gps : NULL)) == -1)
+ {
+ if (gps)
+ {
+ free(*gps);
+ *gps = NULL; // avoid double free later on
+ }
+
+ goto close_and_return; // inconsistent data received from atopgpud
+ }
free(buf);
- return pids;
+ return nrprocs;
close_and_return:
- if (buf)
- free(buf);
+ free(buf);
close(actsock);
actsock = -1;
+ numgpus = 0;
return -1;
}
@@ -314,6 +328,8 @@ gpud_statresponse(int maxgpu, struct pergpu *ggs, struct gpupidstat **gps)
/*
** Receive given number of bytes from given socket
** into given buffer address
+** Return value: number of bytes received
+** -1 - failed (including end-of-connection)
*/
static int
rcvuntil(int sock, char *buf, int size)
@@ -339,21 +355,22 @@ rcvuntil(int sock, char *buf, int size)
**
** Store the type, busid and tasksupport of every GPU in
** static pointer tables
+**
+** Return value: 1 - success
+** 0 - failed
*/
-static void
+static int
gputype_parse(char *buf)
{
- char *p, *start, **bp, **tp, *cp;
+ char *p, *start, **bp, **tp, *cp, fails=0;
/*
** determine number of GPUs
*/
if ( sscanf(buf, "%d@", &numgpus) != 1)
- {
- close(actsock);
- actsock = -1;
return;
- }
+
+ numgpus = numgpus <= MAXGPU ? numgpus : MAXGPU;
for (p=buf; *p; p++) // search for first delimiter
{
@@ -364,6 +381,9 @@ gputype_parse(char *buf)
}
}
+ if (*p == 0) // no delimiter or no data behind delimeter?
+ return 0;
+
/*
** parse GPU info and build arrays of pointers to the
** busid strings, type strings and tasksupport strings.
@@ -380,27 +400,47 @@ gputype_parse(char *buf)
ptrverify(gputypes, "Malloc failed for gpu types\n");
ptrverify(gputasks, "Malloc failed for gpu tasksup\n");
- for (field=0, start=p; ; p++)
+ for (field=0, start=p; fails == 0; p++)
{
if (*p == ' ' || *p == '\0' || *p == GPUDELIM)
{
switch(field)
{
case 0:
+ if (bp - gpubusid >= numgpus)
+ {
+ fails++;
+ break; // inconsistent with number of GPUs
+ }
+
if (p-start <= MAXGPUBUS)
*bp++ = start;
else
*bp++ = p - MAXGPUBUS;
break;
case 1:
+ if (tp - gputypes >= numgpus)
+ {
+ fails++;
+ break; // inconsistent with number of GPUs
+ }
+
if (p-start <= MAXGPUTYPE)
*tp++ = start;
else
*tp++ = p - MAXGPUTYPE;
break;
case 2:
+ if (cp - gputasks >= numgpus)
+ {
+ fails++;
+ break; // inconsistent with number of GPUs
+ }
+
*cp++ = *start;
break;
+ default:
+ fails++;
}
field++;
@@ -418,7 +458,24 @@ gputype_parse(char *buf)
*bp = NULL;
*tp = NULL;
+ /*
+ ** verify if number of GPUs and supplied per-GPU information
+ ** appears to be inconsistent
+ */
+ if (fails || bp - gpubusid != numgpus || tp - gputypes != numgpus || cp - gputasks != numgpus)
+ {
+ free(gpubusid);
+ free(gputypes);
+ free(gputasks);
+ return 0;
+ }
+ }
+ else
+ {
+ return 0;
}
+
+ return 1;
}
@@ -430,105 +487,140 @@ gputype_parse(char *buf)
** Every series with counters on process level is introduced
** with a '#' delimiter (last part of the GPU level data).
*/
-static void
+static int
gpustat_parse(int version, char *buf, int maxgpu,
struct pergpu *gg, struct gpupidstat *gp)
{
- char *p, *start, delimlast;
- int gpunum = 0;
+ char *p, *pp, *start;
+ int gpunum, nrprocs = 0;
/*
** parse stats string
*/
- for (p=start=buf, delimlast=DUMMY; gpunum <= maxgpu; p++)
+ for (p=buf; *p && *p != GPUDELIM; p++) // find first GPU deimiter
+ ;
+
+ if (*p == 0) // string without GPU delimiter
+ return -1;
+
+ for (p++, start=p, gpunum=0; gpunum < maxgpu; p++)
{
- char delimnow;
+ char delimnext;
- if (*p != '\0' && *p != GPUDELIM && *p != PIDDELIM)
+ // search next GPU delimiter
+ //
+ if (*p && *p != GPUDELIM)
continue;
/*
- ** next delimiter or end-of-string found
+ ** next GPU delimiter or end-of-string found
*/
- delimnow = *p;
+ delimnext = *p;
*p = 0;
- switch (delimlast)
- {
- case DUMMY:
- break;
-
- case GPUDELIM:
- gpuparse(version, start, gg);
-
- strcpy(gg->type, gputypes[gpunum]);
- strcpy(gg->busid, gpubusid[gpunum]);
+ /*
+ ** parse GPU itself
+ */
+ if (! gpuparse(version, start, gg))
+ return -1;
- gpunum++;
- gg++;
- break;
+ strncpy(gg->type, gputypes[gpunum], MAXGPUTYPE);
+ strncpy(gg->busid, gpubusid[gpunum], MAXGPUBUS);
- case PIDDELIM:
- if (gp)
+ /*
+ ** continue searching for per-process stats for this GPU
+ */
+ if (gp)
+ {
+ for (pp = start; pp < p; pp++)
{
- pidparse(version, start, gp);
+ if (*pp != PIDDELIM)
+ continue;
+
+ // new PID delimiter (#) found
+ //
+ if (! pidparse(version, pp+1, gp))
+ return -1;
gp->gpu.nrgpus++;
- gp->gpu.gpulist = 1<<(gpunum-1);
+ gp->gpu.gpulist = 1<<gpunum;
gp++;
- (gg-1)->nrprocs++;
+ gg->nrprocs++; // per GPU
+ nrprocs++; // total
}
}
- if (delimnow == 0 || *(p+1) == 0)
+ gpunum++;
+ gg++;
+
+ if (delimnext == 0 || *(p+1) == 0)
break;
start = p+1;
- delimlast = delimnow;
}
+ return nrprocs;
}
/*
** Parse GPU statistics string
+** Return value: 1 - success
+** 0 - failed
*/
-static void
+static int
gpuparse(int version, char *p, struct pergpu *gg)
{
+ int nr;
+
switch (version)
{
case 1:
- (void) sscanf(p, "%d %d %lld %lld %lld %lld %lld %lld",
+ nr = sscanf(p, "%d %d %lld %lld %lld %lld %lld %lld",
&(gg->gpupercnow), &(gg->mempercnow),
&(gg->memtotnow), &(gg->memusenow),
&(gg->samples), &(gg->gpuperccum),
&(gg->memperccum), &(gg->memusecum));
+ if (nr < 8) // parse error: unexpected data
+ return 0;
+
gg->nrprocs = 0;
break;
}
+
+ return 1;
}
/*
** Parse PID statistics string
+** Return value: 1 - success
+** 0 - failed
*/
-static void
+static int
pidparse(int version, char *p, struct gpupidstat *gp)
{
+ int nr;
+
switch (version)
{
case 1:
- (void) sscanf(p, "%c %ld %d %d %lld %lld %lld %lld",
+ nr = sscanf(p, "%c %ld %d %d %lld %lld %lld %lld",
&(gp->gpu.state), &(gp->pid),
&(gp->gpu.gpubusy), &(gp->gpu.membusy),
&(gp->gpu.timems),
&(gp->gpu.memnow), &(gp->gpu.memcum),
&(gp->gpu.sample));
+
+ if (nr < 8) // parse error: unexpected data
+ return 0;
+
break;
}
+
+ return 1;
}
diff --git a/photoproc.c b/photoproc.c
index e5cd88b..5df04e3 100644
--- a/photoproc.c
+++ b/photoproc.c
@@ -216,7 +216,8 @@ photoproc(struct tstat *tasklist, int maxtask)
*/
regainrootprivs();
- netatop_probe();
+ if (connectnetatop)
+ netatop_probe();
if (! droprootprivs())
cleanstop(42);

View File

@ -20,6 +20,7 @@ SRC_URI = "http://www.atoptool.nl/download/${BP}.tar.gz \
file://fix-permissions.patch \
file://sysvinit-implement-status.patch \
file://0001-atop.daily-atop.init-atop-pm.sh-Avoid-using-bash.patch \
file://CVE-2025-31160.patch \
"
SRC_URI[md5sum] = "1077da884ed94f2bc3c81ac3ab970436"
SRC_URI[sha256sum] = "be1c010a77086b7d98376fce96514afcd73c3f20a8d1fe01520899ff69a73d69"