fix a lot of libhtml leaks Reference: /n/atom/patch/applied2013/htmlleaks Date: Thu Dec 5 01:56:02 CET 2013 Signed-off-by: quanstro@quanstro.net --- /sys/src/libhtml/build.c Thu Dec 5 01:55:53 2013 +++ /sys/src/libhtml/build.c Thu Dec 5 01:55:53 2013 @@ -54,6 +54,7 @@ int ntables; int nanchors; int nframes; + Formfield* curfield; Form* curform; Map* curmap; Table* tabstk; @@ -358,6 +359,7 @@ is->ntables = 0; is->nanchors = 0; is->nframes = 0; + is->curfield = nil; is->curform = nil; is->curmap = nil; is->tabstk = nil; @@ -365,6 +367,62 @@ return is; } +static void +linkitems(Docinfo *di, Item *it) +{ + Formfield *ff; + Tablecell *c; + Table *tt; + + while(it != nil){ + switch(it->tag) { + case Iimagetag: + /* link image to docinfo */ + ((Iimage*)it)->nextimage = di->images; + di->images = (Iimage*)it; + break; + case Iformfieldtag: + /* link formfield to form */ + ff = ((Iformfield*)it)->formfield; + if(ff != nil && ff->form != nil){ + for(ff = ff->form->fields; ff != nil; ff = ff->next){ + if(ff == ((Iformfield*)it)->formfield) + goto Next; + if(ff->next == nil) + break; + } + ((Iformfield*)it)->formfield->next = nil; + if(ff != nil){ + ff->next = ((Iformfield*)it)->formfield; + ff = ff->next; + } else { + ff = ((Iformfield*)it)->formfield; + ff->form->fields = ff; + } + linkitems(di, ff->image); + } + break; + case Itabletag: + /* link table to docinfo */ + tt = ((Itable*)it)->table; + if(tt == nil) + break; + tt->tabletok = nil; + tt->next = di->tables; + di->tables = tt; + linkitems(di, tt->caption); + for(c = tt->cells; c != nil; c = c->next) + linkitems(di, c->content); + break; + case Ifloattag: + linkitems(di, ((Ifloat*)it)->item); + break; + } + Next: + it = it->next; + } +} + static Item *getitems(ItemSource* is, uchar* data, int datalen); // Parse an html document and create a list of layout items. @@ -453,7 +511,6 @@ Rune* script; Map* map; Form* frm; - Iimage* ii; Kidinfo* kd; Kidinfo* ks; Kidinfo* pks; @@ -694,12 +751,8 @@ bgurl = aurlval(tok, Abackground, nil, di->base); if(bgurl != nil) { if(di->backgrounditem != nil) - freeitem((Item*)di->backgrounditem); - // really should remove old item from di->images list, - // but there should only be one BODY element ... + freeitem(di->backgrounditem); di->backgrounditem = (Iimage*)newiimage(bgurl, nil, ALnone, 0, 0, 0, 0, 0, 0, nil); - di->backgrounditem->nextimage = di->images; - di->images = di->backgrounditem; } ps->curbg = bg; di->background = bg; @@ -747,8 +800,11 @@ fprint(2, "warning: unexpected \n"); continue; } + if(curtab->caption != nil) + freeitems(curtab->caption); curtab->caption = ps->items->next; - free(ps); + ps->items->next = nil; + freepstate(ps); ps = nextps; break; @@ -893,8 +949,6 @@ fprint(2, "warning: unexpected \n"); continue; } - // put fields back in input order - is->curform->fields = (Formfield*)_revlist((List*)is->curform->fields); is->curform = nil; break; @@ -1083,10 +1137,6 @@ ps->skipwhite = 0; additem(ps, img, tok); } - if(!ps->skipping) { - ((Iimage*)img)->nextimage = di->images; - di->images = (Iimage*)img; - } ps->curanchor = oldcuranchor; break; @@ -1098,7 +1148,7 @@ fprint(2, " not inside
\n"); continue; } - is->curform->fields = field = newformfield( + field = newformfield( atabval(tok, Atype, input_tab, NINPUTTAB, Ftext), ++is->curform->nfields, is->curform, @@ -1106,7 +1156,7 @@ aval(tok, Avalue), auintval(tok, Asize, 0), auintval(tok, Amaxlength, 1000), - is->curform->fields); + nil); if(aflagval(tok, Achecked)) field->flags = FFchecked; @@ -1158,9 +1208,6 @@ atabval(tok, Aalign, align_tab, NALIGNTAB, ALbottom), auintval(tok, Awidth, 0), auintval(tok, Aheight, 0), 0, 0, 0, 0, nil); - ii = (Iimage*)field->image; - ii->nextimage = di->images; - di->images = ii; break; case Freset: @@ -1187,21 +1234,19 @@ additem(ps, textit(ps, prompt), tok); frm = newform(++is->nforms, nil, - di->base, + _Strdup(di->base), target, HGet, di->forms); di->forms = frm; ff = newformfield(Ftext, - 1, + ++frm->nfields, frm, _Strdup(L"_ISINDEX_"), nil, 50, 1000, nil); - frm->fields = ff; - frm->nfields = 1; additem(ps, newiformfield(ff), tok); addbrk(ps, 1, 0); break; @@ -1398,15 +1443,14 @@ fprint(2, "\n"); continue; } - field = is->curform->fields; + field = is->curfield; if(field->ftype != Fselect) continue; // put options back in input order field->options = (Option*)_revlist((List*)field->options); + is->curfield = nil; break; // @@ -1544,8 +1589,6 @@ } else is->tabstk = is->tabstk->next; - curtab->next = di->tables; - di->tables = curtab; curtab = is->tabstk; if(!isempty) addbrk(ps, 0, 0); @@ -1640,8 +1683,7 @@ nil, 0, 0, - is->curform->fields); - is->curform->fields = field; + nil); field->rows = auintval(tok, Arows, 3); field->cols = auintval(tok, Acols, 50); field->value = getpcdata(toks, tokslen, &toki); @@ -1694,6 +1736,7 @@ fprint(2, "warning: empty row\n"); curtab->rows = tr->next; tr->next = nil; + free(tr); } else tr->flags = 0; @@ -1793,8 +1836,6 @@ } if(is->tabstk != nil) is->tabstk = is->tabstk->next; - curtab->next = di->tables; - di->tables = curtab; curtab = is->tabstk; } outerps = lastps(ps); @@ -1803,6 +1844,7 @@ // note: ans may be nil and di->kids not nil, if there's a frameset! outerps->items = newispacer(ISPnull); outerps->lastit = outerps->items; + outerps->prelastit = nil; is->psstk = ps; if(ans != nil && di->hasscripts) { // TODO evalscript(nil); @@ -1817,6 +1859,8 @@ else printitems(ans, "getitems returning:"); } + linkitems(di, ans); + _freetokens(toks, tokslen); return ans; } @@ -1892,7 +1936,10 @@ fprint(2, "warning: parse state stack is wrong\n"); } else { + if(c->content != nil) + freeitems(c->content); c->content = psstk->items->next; + psstk->items->next = nil; c->flags &= ~TFparsing; freepstate(psstk); psstk = psstknext; @@ -1977,6 +2024,7 @@ if(ps->skipping) { if(warn) fprint(2, "warning: skipping item: %I\n", it); + freeitem(it); return; } it->anchorid = ps->curanchor; @@ -2213,6 +2261,7 @@ // try to avoid making empty items // but not crucial f the occasional one gets through if(nl == 0 && ps->prelastit != nil) { + freeitems(ps->lastit); ps->lastit = ps->prelastit; ps->lastit->next = nil; ps->prelastit = nil; @@ -2474,6 +2523,7 @@ // copy the data from the allocated Tablerow into the array slot t->rows[r] = *row; rownext = row->next; + free(row); row = &t->rows[r]; r--; rcols = 0; @@ -2482,7 +2532,7 @@ // If rowspan is > 1 but this is the last row, // reset the rowspan if(c != nil && c->rowspan > 1 && r == nrow-2) - c->rowspan = 1; + c->rowspan = 1; // reverse row->cells list (along nextinrow pointers) row->cells = nil; @@ -3220,6 +3270,7 @@ free(ga->style); free(ga->title); freescriptevents(ga->events); + free(ga); } free(it); } @@ -3250,6 +3301,7 @@ free(ff->name); free(ff->value); + freeitem(ff->image); for(o = ff->options; o != nil; o = onext) { onext = o->next; free(o->value); @@ -3273,6 +3325,7 @@ for(c = t->cells; c != nil; c = cnext) { cnext = c->next; freeitems(c->content); + free(c); } if(t->grid != nil) { for(i = 0; i < t->nrow; i++) @@ -3428,7 +3481,8 @@ return; free(d->src); free(d->base); - freeitem((Item*)d->backgrounditem); + free(d->doctitle); + freeitem(d->backgrounditem); free(d->refresh); freekidinfos(d->kidinfo); freeanchors(d->anchors); @@ -3440,11 +3494,10 @@ free(d); } -// Currently, someone else owns all the memory -// pointed to by things in a Pstate. static void freepstate(Pstate* p) { + freeitems(p->items); free(p); } @@ -3456,7 +3509,7 @@ for(p = pshead; p != nil; p = pnext) { pnext = p->next; - free(p); + freepstate(p); } } @@ -3717,7 +3770,6 @@ t->caption_place = ALbottom; t->caption_lay = nil; t->tabletok = tok; - t->tabletok = nil; t->next = link; return t; } --- /sys/src/libhtml/lex.c Thu Dec 5 01:55:53 2013 +++ /sys/src/libhtml/lex.c Thu Dec 5 01:55:53 2013 @@ -568,6 +568,13 @@ lexinited = 1; } +static void +freetoken(Token *t) +{ + free(t->text); + freeattrs(t->attr); +} + static TokenSource* newtokensource(uchar* data, int edata, int chset, int mtype) { @@ -671,7 +678,7 @@ static int getplaindata(TokenSource* ts, Token* a, int* pai) { - Rune* s; + Rune* s, *s0; int j; int starti; int c; @@ -701,14 +708,18 @@ if(c != 0) { buf[j++] = c; if(j == nelem(buf)-1) { + s0 = s; s = buftostr(s, buf, j); + free(s0); j = 0; } } if(c == '\n') break; } + s0 = s; s = buftostr(s, buf, j); + free(s0); if(s == nil) return -1; tok = &a[(*pai)++]; @@ -723,11 +734,15 @@ static Rune* buftostr(Rune* s, Rune* buf, int j) { + uintptr pc; + + pc = getcallerpc(&s); buf[j] = 0; if(s == nil) s = _Strndup(buf, j); else s = _Strdup2(s, buf); + setmalloctag(s, pc); return s; } @@ -739,7 +754,7 @@ static int getdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai) { - Rune* s; + Rune* s, *s0, *text; int j; int c; Token* tok; @@ -779,17 +794,20 @@ if(c != 0) { buf[j++] = c; if(j == nelem(buf)-1) { + s0 = s; s = buftostr(s, buf, j); + free(s0); j = 0; } } } - s = buftostr(s, buf, j); - if(s == nil) + text = buftostr(s, buf, j); + free(s); + if(text == nil) return -1; tok = &a[(*pai)++]; tok->tag = Data; - tok->text = s; + tok->text = text; tok->attr = nil; tok->starti = starti; return Data; @@ -800,7 +818,7 @@ static int getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai, int findtag) { - Rune* s; + Rune* s, *s0; int j; int tstarti; int savei; @@ -833,8 +851,10 @@ tag = gettag(ts, tstarti, a, pai); if(tag == -1) break; - if(tag != Comment) + if(tag != Comment){ (*pai)--; + freetoken(&a[*pai]); + } backup(ts, tstarti); if(tag == findtag + RBRA) { done = 1; @@ -849,7 +869,9 @@ if(c != 0) { buf[j++] = c; if(j == nelem(buf)-1) { + s0 = s; s = buftostr(s, buf, j); + free(s0); j = 0; } } @@ -857,7 +879,9 @@ c = getchar(ts); } if(done || ts->i == ts->edata) { + s0 = s; s = buftostr(s, buf, j); + free(s0); tok = &a[(*pai)++]; tok->tag = Data; tok->text = s; @@ -889,13 +913,14 @@ int afnd; int attid; int quote; - Rune* val; + Rune* val, *s0; int nv; int i; int tag; Token* tok; Rune buf[BIGBUFSIZE]; + al = nil; rbra = 0; nexti = ts->i; tok = &a[*pai]; @@ -922,6 +947,7 @@ backup(ts, nexti); tok->tag = Data; tok->text = _Strdup(L"<"); + setmalloctag(tok->text, getcallerpc(&ts)); (*pai)++; return Data; } @@ -944,7 +970,6 @@ else tok->text = _Strndup(buf, i); // for warning print, in build // attribute gathering loop - al = nil; while(1) { // look for "ws name" or "ws name ws = ws val" (ws=whitespace) // skip whitespace @@ -1045,7 +1070,9 @@ backup(ts, ti); buf[nv++] = '>'; if(nv == BIGBUFSIZE-1) { + s0 = val; val = buftostr(val, buf, nv); + free(s0); nv = 0; } c = getchar(ts); @@ -1088,14 +1115,18 @@ } buf[nv++] = c; if(nv == BIGBUFSIZE-1) { + s0 = val; val = buftostr(val, buf, nv); + free(s0); nv = 0; } c = getchar(ts); } valloop_done: if(afnd) { + s0 = val; val = buftostr(val, buf, nv); + free(s0); al = newattr(attid, val, al); } } @@ -1109,6 +1140,7 @@ if(warn) fprint(2, "warning: incomplete tag at end of page\n"); backup(ts, nexti); + freeattrs(al); tok->tag = Data; tok->text = _Strdup(L"<"); return Data; @@ -1307,9 +1339,9 @@ break; case UTF_8: ok = fullrune((char*)(buf+ts->i), ts->edata-ts->i); - n = chartorune(&r, (char*)(buf+ts->i)); if(ok) { - if(warn && c == 0x80) + n = chartorune(&r, (char*)(buf+ts->i)); + if(warn && c == Runeerror) fprint(2, "warning: invalid utf-8 sequence (starts with %x)\n", ts->data[ts->i]); ts->i += n; c = r; @@ -1458,6 +1490,7 @@ ans->attid = attid; ans->value = value; ans->next = link; + setmalloctag(ans, getcallerpc(&attid)); return ans; } --- /sys/src/libhtml/utils.c Thu Dec 5 01:55:53 2013 +++ /sys/src/libhtml/utils.c Thu Dec 5 01:55:53 2013 @@ -349,9 +349,13 @@ Rune* _Strdup(Rune* s) { + Rune* ans; + if(s == nil) return nil; - return _Strndup(s, runestrlen(s)); + ans = _Strndup(s, runestrlen(s)); + setmalloctag(ans, getcallerpc(&s)); + return ans; } // emalloc and copy n chars of s (assume s is at least that long), @@ -367,6 +371,7 @@ ans = _newstr(n); memmove(ans, s, n*sizeof(Rune)); ans[n] = 0; + setmalloctag(ans, getcallerpc(&s)); return ans; } // emalloc enough room for n Runes, plus 1 null terminator. @@ -374,7 +379,11 @@ Rune* _newstr(int n) { - return (Rune*)emalloc((n+1)*sizeof(Rune)); + Rune* ans; + + ans = (Rune*)emalloc((n+1)*sizeof(Rune)); + setmalloctag(ans, getcallerpc(&n)); + return ans; } // emalloc and copy s+t @@ -393,6 +402,7 @@ p = _Stradd(ans, s, ns); p = _Stradd(p, t, nt); *p = 0; + setmalloctag(ans, getcallerpc(&s)); return ans; } @@ -405,6 +415,7 @@ if(start == stop) return nil; t = _Strndup(s+start, stop-start); + setmalloctag(t, getcallerpc(&s)); return t; } @@ -551,12 +562,13 @@ ans = nil; assert(0); } + setmalloctag(ans, getcallerpc(&buf)); return ans; } // Convert buf[0:n], Unicode characters, // into an emalloc'd null-terminated string in character set chset. -// Use 0x80 for unconvertable characters. +// Use Runeerror for unconvertable characters. uchar* fromStr(Rune* buf, int n, int chset) { @@ -575,7 +587,7 @@ for(i = 0; i < n; i++) { ch = buf[i]; if(ch > lim) - ch = 0x80; + ch = Runeerror; ans[i] = ch; } ans[n] = 0; @@ -596,6 +608,6 @@ default: assert(0); } + setmalloctag(ans, getcallerpc(&buf)); return ans; - }