Files
knutwurst--ps5-patchdl/src/patchdl_proc.c
T
Knutwurst 9006965a75 Add download cancel/delete and harden the patch pipeline
Cancel a running download (the worker aborts mid-piece and the partial
file is removed) or delete a finished package, from the queue or the
title card. The progress callback now returns an abort signal that
reaches libcurl and the manifest merge loop.

Manifest merge: bound the piece scan to the "pieces" array so a later
"url" key (e.g. playgoChunkCrcUrl) can't be appended as a bogus piece,
and require each piece's fileOffset to match the bytes written so far so
an out-of-order manifest fails instead of silently producing a corrupt
package.

Report real free space on the download partition via statvfs; it was a
hardcoded 0.

Fixes found in review:
- scan: bound the SFO entry table to the bytes actually read and require
  the key to be NUL-terminated before strcmp (OOB read on a crafted
  param.sfo from a shadow-mounted dir).
- proc: bound the kinfo_proc walk and the name compare to the record and
  the buffer.
- install: publish the API probe under the lock (data race with the MHD
  worker thread) and initialize rc2.
- verxml: reject a truncated attribute value instead of returning it as
  valid.
- web: keep download/install/downloaded flags across a refresh, stop the
  queue poll only after repeated empty results, coerce the progress
  number, and treat a cancelled download (HTTP 200, ok:false) as
  not-downloaded.
2026-06-23 17:52:29 +02:00

76 lines
2.2 KiB
C

#include "patchdl_proc.h"
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/sysctl.h>
/* kinfo_proc field offsets for the FreeBSD 9-based PS5 kernel.
Same layout used by ps5-payload-dev/ftpsrv (verified on fw 11.60):
ki_pid at +72, ki_tdname (thread name) at +447, ki_structsize at +0. */
#define KINFO_OFF_STRUCTSIZE 0
#define KINFO_OFF_PID 72
#define KINFO_OFF_TDNAME 447
void
patchdl_proc_set_name(const char *name) {
/* tid -1 = current thread */
syscall(SYS_thr_set_name, -1, name);
}
static pid_t
find_pid(const char *name) {
int mib[4] = {1, 14, 8, 0}; /* CTL_KERN, KERN_PROC, KERN_PROC_ALL */
pid_t mypid = getpid();
pid_t pid = -1;
size_t buf_size;
uint8_t *buf;
if (sysctl(mib, 4, 0, &buf_size, 0, 0)) return -1;
if (!(buf = malloc(buf_size))) return -1;
if (sysctl(mib, 4, buf, &buf_size, 0, 0)) { free(buf); return -1; }
/* The loop guard guarantees the structsize/pid/tdname fields are inside the
buffer before we read them, and the per-record check below keeps the name
compare within the record (and thus the buffer). */
for (uint8_t *ptr = buf; ptr + KINFO_OFF_TDNAME < buf + buf_size; ) {
int ki_structsize = *(int *)(ptr + KINFO_OFF_STRUCTSIZE);
pid_t ki_pid;
char *ki_tdname;
size_t name_max;
if (ki_structsize <= KINFO_OFF_TDNAME) break; /* malformed/truncated */
if (ptr + ki_structsize > buf + buf_size) break; /* record past buffer */
ki_pid = *(pid_t *)(ptr + KINFO_OFF_PID);
ki_tdname = (char *)(ptr + KINFO_OFF_TDNAME);
name_max = (size_t)(ptr + ki_structsize - (uint8_t *)ki_tdname);
if (!strncmp(name, ki_tdname, name_max) && ki_pid != mypid)
pid = ki_pid;
ptr += ki_structsize;
}
free(buf);
return pid;
}
int
patchdl_proc_kill_others(const char *name) {
int killed = 0;
pid_t pid;
while ((pid = find_pid(name)) > 0) {
if (kill(pid, SIGKILL))
break;
killed++;
sleep(1); /* let the port + process slot free up */
}
return killed;
}