fix(jsonlite/discord): two memory-safety bugs found via host ASan

1. jsonlite.c: jl_stringify started with out=NULL but emit's growth
   check (1 > cap) never triggered realloc, so the first '{' wrote to
   NULL -> guaranteed crash on first IDENTIFY/presence build. Pre-allocate.
   Also grow the buffer in the no-child fallback path.

2. discord.c: jl_obj_set(dd,"status","online") and "invisible"
   passed a const char* where jl_val_t* is expected. Compiles (both are
   pointers) but pair->child points at rodata -> jl_free reads garbage
   struct fields, double-free/global-buffer-overflow caught by ASan.

Verified with host-side ASan tests: presence payload roundtrips clean,
1000 READY parse/free cycles leak-free, b64 known-answer vectors pass.
This commit is contained in:
SirHumza committed 2026-08-14 10:30:25 +02:00
1 parent c41c7ab186
commit 7622c4915b
2 files changed
+7 -4

No files matched your search

+2 -2
View File
@@ -87,7 +87,7 @@ int discord_set_presence(discord_t *d, const char *state, const char *details,
jl_obj_set(act,"assets",assets);
jl_obj_set(dd,"activities",jl_new_array());
jl_arr_push(jl_obj_get(dd,"activities"), act);
jl_obj_set(dd,"status","online");
jl_obj_set(dd,"status",jl_new_string("online"));
jl_obj_set(dd,"since",jl_new_number((double)time(NULL)));
jl_obj_set(dd,"afk",jl_new_bool(0));
jl_val_t *root=jl_new_object();
@@ -101,7 +101,7 @@ int discord_set_presence(discord_t *d, const char *state, const char *details,
int discord_clear_presence(discord_t *d){
jl_val_t *dd=jl_new_object();
jl_obj_set(dd,"activities",jl_new_array());
jl_obj_set(dd,"status","invisible");
jl_obj_set(dd,"status",jl_new_string("invisible"));
jl_val_t *root=jl_new_object();
jl_obj_set(root,"op",jl_new_number(3));
jl_obj_set(root,"d",dd);
+5 -2
View File
@@ -205,7 +205,9 @@ static void emit(jl_val_t *v, char **out, size_t *cap, size_t *len){
escstr(p->str,out,cap,len);
size_t l3=*len+1; if(l3>*cap){*cap=l3*2;*out=realloc(*out,*cap);} (*out)[(*len)++]=':';
/* value is the pair's child OR the pair holds value via child */
if(p->child) emit(p->child,out,cap,len); else { *out[*len]='n'; /* shouldn't happen */ }
if(p->child) emit(p->child,out,cap,len); else {
size_t lz=*len+1; if(lz>*cap){*cap=lz*2;*out=realloc(*out,*cap);} (*out)[(*len)++]='n';
}
/* for non-pair objects where a STRING key sits directly: handled above via child */
} p=p->next; }
l=*len+1; if(l>*cap){*cap=l*2;*out=realloc(*out,*cap);} (*out)[(*len)++]='}'; return; }
@@ -216,7 +218,8 @@ static void emit(jl_val_t *v, char **out, size_t *cap, size_t *len){
}
char *jl_stringify(const jl_val_t *v){
char *out=NULL; size_t cap=256,len=0;
char *out=(char*)malloc(256); if(!out) return NULL;
size_t cap=256,len=0;
emit((jl_val_t*)v,&out,&cap,&len);
if(!out){ out=(char*)malloc(1); out[0]=0; }
else { char *t=(char*)realloc(out,len+1); t[len]=0; out=t; }