From 0f5d5c85413074673422c7081fc7fa8353df8374 Mon Sep 17 00:00:00 2001 From: woolcoxm Date: Tue, 14 Jul 2026 09:43:20 -0400 Subject: [PATCH] loaders: fix silent short-read (uninitialized memory) in config/oracle readers (#181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cfg_load() (line ~911, reads config.json at every startup) and the oracle reference loader (line ~3846, reads ref_glm.json) both had: char *b=malloc(n+1); if(fread(b,1,n,f)!=(size_t)n){} b[n]=0; fclose(f); The empty if-body {} silently ignored a short read. If fread returned fewer bytes than n (truncated file, disk error, concurrent modification), b[n]=0 wrote a null terminator at position n — but only bytes 0..got-1 were valid. json_parse then read uninitialized memory between got and n. Fixed to null-terminate at the actual read position and warn on short read: size_t got=fread(b,1,n,f); b[got]=0; fclose(f); if((long)got!=n) fprintf(stderr,"warning: short read on %s ..."); Two instances fixed: cfg_load (config.json) and the ref_glm.json oracle reader. Co-authored-by: woolcoxm <13604288+woolcoxm@users.noreply.github.com> --- c/glm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/c/glm.c b/c/glm.c index bbebd81..c3650bc 100644 --- a/c/glm.c +++ b/c/glm.c @@ -912,7 +912,8 @@ static jval* cfg_root(const char *snap, char **arena){ char p[2048]; snprintf(p,sizeof(p),"%s/config.json",snap); FILE *f=fopen(p,"rb"); if(!f){perror(p);exit(1);} fseek(f,0,SEEK_END); long n=ftell(f); fseek(f,0,SEEK_SET); - char *b=malloc(n+1); if(fread(b,1,n,f)!=(size_t)n){} b[n]=0; fclose(f); + char *b=malloc(n+1); size_t got=fread(b,1,n,f); b[got]=0; fclose(f); + if((long)got!=n) fprintf(stderr,"warning: short read on %s (%ld of %ld)\n",p,(long)got,n); return json_parse(b,arena); } static int gi(jval*r,const char*k){ jval*v=json_get(r,k); return v?(int)v->num:0; } @@ -3849,7 +3850,8 @@ int main(int argc, char **argv){ const char *refpath=getenv("REF")?getenv("REF"):"ref_glm.json"; FILE *f=fopen(refpath,"rb"); if(!f){perror(refpath);return 1;} fseek(f,0,SEEK_END); long n=ftell(f); fseek(f,0,SEEK_SET); - char *b=malloc(n+1); if(fread(b,1,n,f)!=(size_t)n){} b[n]=0; fclose(f); + char *b=malloc(n+1); size_t got=fread(b,1,n,f); b[got]=0; fclose(f); + if((long)got!=n) fprintf(stderr,"warning: short read on %s (%ld of %ld)\n",refpath,(long)got,n); char *ar=NULL; jval *ref=json_parse(b,&ar); int np,nfull; int *prompt=read_arr(ref,"prompt_ids",&np); int *full=read_arr(ref,"full_ids",&nfull); int n_new=nfull-np;