Version in base suite: 2.8.1-1 Base version: atop_2.8.1-1 Target version: atop_2.8.1-1+deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/a/atop/atop_2.8.1-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/a/atop/atop_2.8.1-1+deb12u1.dsc changelog | 6 patches/fix-cve-2025-31160 | 625 +++++++++++++++++++++++++++++++++++++++++++++ patches/series | 1 3 files changed, 632 insertions(+) diff -Nru atop-2.8.1/debian/changelog atop-2.8.1/debian/changelog --- atop-2.8.1/debian/changelog 2023-01-23 13:45:02.000000000 +0000 +++ atop-2.8.1/debian/changelog 2025-04-01 06:25:40.000000000 +0000 @@ -1,3 +1,9 @@ +atop (2.8.1-1+deb12u1) bookworm-security; urgency=high + + * add upstream patch fixing CVE-2025-31160 + + -- Marc Haber Tue, 01 Apr 2025 08:25:40 +0200 + atop (2.8.1-1) unstable; urgency=medium * new upstream version 2.8.1 diff -Nru atop-2.8.1/debian/patches/fix-cve-2025-31160 atop-2.8.1/debian/patches/fix-cve-2025-31160 --- atop-2.8.1/debian/patches/fix-cve-2025-31160 1970-01-01 00:00:00.000000000 +0000 +++ atop-2.8.1/debian/patches/fix-cve-2025-31160 2025-04-01 06:25:40.000000000 +0000 @@ -0,0 +1,625 @@ +--- a/atop.c ++++ b/atop.c +@@ -324,6 +324,8 @@ char calcpss = 0; /* boolean: + char getwchan = 0; /* boolean: obtain wchan string */ + char rmspaces = 0; /* boolean: remove spaces from command */ + /* name in case of parseable output */ ++char connectgpud = 0; /* boolean: connect to atopgpud */ ++char connectnetatop = 0; /* boolean: connect to netatop(bpf) */ + + unsigned short hertz; + unsigned int pidwidth; +@@ -619,6 +621,14 @@ main(int argc, char *argv[]) + rmspaces = 1; + 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; + } +@@ -736,7 +746,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 +@@ -839,11 +850,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: +@@ -890,7 +905,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 +@@ -915,28 +933,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; +@@ -1025,7 +1023,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); +@@ -1056,8 +1054,8 @@ engine(void) + if (nprocexitnet > 0) + netatop_exiterase(); + +- if (gp) +- free(gp); ++ free(gp); ++ gp = NULL; // avoid double free + + if (lastcmd == 'r') /* reset requested ? */ + { +@@ -1102,6 +1100,8 @@ prusage(char *myname) + MRMSPACES); + 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"); + + if (vis.show_usage) + (*vis.show_usage)(); +--- a/atop.h ++++ b/atop.h +@@ -89,6 +89,7 @@ extern char calcpss; + extern char getwchan; + extern char rawname[]; + extern char rawreadflag; ++extern char connectnetatop; + extern char rmspaces; + extern time_t begintime, endtime, cursortime; // epoch or time in day + extern char flaglist[]; +--- a/deviate.c ++++ b/deviate.c +@@ -1053,7 +1053,7 @@ deviatsyst(struct sstat *cur, struct sst + /* + ** determine new j + */ +- if (pre->dsk.dsk[j].name) // existing matching entry ++ if (pre->dsk.dsk[j].name[0]) // existing matching entry + j++; + else + j = realj; // empty entry: stick to old j +@@ -1122,7 +1122,7 @@ deviatsyst(struct sstat *cur, struct sst + /* + ** determine new j + */ +- if (pre->dsk.mdd[j].name) // existing matching entry ++ if (pre->dsk.mdd[j].name[0]) // existing matching entry + j++; + else + j = realj; // empty entry: stick to old j +@@ -1191,7 +1191,7 @@ deviatsyst(struct sstat *cur, struct sst + /* + ** determine new j + */ +- if (pre->dsk.lvm[j].name) // existing matching entry ++ if (pre->dsk.lvm[j].name[0]) // existing matching entry + j++; + else + j = realj; // empty entry: stick to old j +--- 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 per + 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 per + *(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 per + /* + ** 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,9 @@ gpud_statresponse(int maxgpu, struct per + /* + ** 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,23 +356,27 @@ 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; +- } ++ return 0; ++ ++ numgpus = numgpus <= MAXGPU ? numgpus : MAXGPU; + +- for (p=buf; *p; p++) // search for first delimiter ++ /* ++ ** search for first GPU delimiter (@) ++ */ ++ for (p=buf; *p; p++) + { + if (*p == GPUDELIM) + { +@@ -364,6 +385,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 +404,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 +462,25 @@ 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; + } + + +@@ -429,106 +491,146 @@ gputype_parse(char *buf) + ** with a '@' delimiter. + ** Every series with counters on process level is introduced + ** with a '#' delimiter (last part of the GPU level data). ++** ++** Return value: valid number of processes ++** -1 - failed + */ +-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; +- *p = 0; +- +- switch (delimlast) +- { +- case DUMMY: +- break; +- +- case GPUDELIM: +- gpuparse(version, start, gg); ++ delimnext = *p; ++ *p = 0; + +- 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<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; ++ start = p+1; + } ++ ++ 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; + } + + +--- a/photoproc.c ++++ b/photoproc.c +@@ -136,7 +136,8 @@ photoproc(struct tstat *tasklist, int ma + */ + regainrootprivs(); + +- netatop_probe(); ++ if (connectnetatop) ++ netatop_probe(); + + if (supportflags & CGROUPV2) + wipecgroupv2(); diff -Nru atop-2.8.1/debian/patches/series atop-2.8.1/debian/patches/series --- atop-2.8.1/debian/patches/series 2023-01-23 13:45:02.000000000 +0000 +++ atop-2.8.1/debian/patches/series 2025-04-01 06:25:40.000000000 +0000 @@ -1,3 +1,4 @@ +fix-cve-2025-31160 atop-rotate-systemctl-path dh_systemd_enable dh_installinit