tats / w3m

Debian's w3m: WWW browsable pager
https://tracker.debian.org/pkg/w3m
Other
873 stars 94 forks source link

heap corruption due to integer overflow in renderTable() #25

Closed kcwu closed 8 years ago

kcwu commented 8 years ago

This bug is interesting since it triggered libgc's issue https://github.com/ivmai/bdwgc/issues/135 as well.

How to reproduce

$ echo '<table width=333330000%><table width=300000><textarea rows=2>' > file.in
$ ./w3m -T text/html -dump file.in

gdb

(gdb) b Strnew_size if n < 0
Breakpoint 1 at 0x4794b8: file Str.c, line 50.
(gdb) r

Breakpoint 1, Strnew_size (n=-3414) at Str.c:50
50          Str x = GC_MALLOC(sizeof(struct _Str));
(gdb) n
51          x->ptr = GC_MALLOC_ATOMIC(n + 1);
(gdb) n
52          x->ptr[0] = '\0';
(gdb) p x->ptr
$1 = 0x7df000 ""

This demonstrate libgc's bug. n+1 == -3413. libgc treat it as unsigned long == 18446744073709548203. The allocation should be failed (either return NULL or abort the program). But it returns 0x7df000.

If continue to run

(gdb) c
Continuing.
Duplicate large block deallocation

Program received signal SIGABRT, Aborted.
0x00007ffff6c70c37 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56      ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.

With further investigation, w3m's negative size comes from table.c, renderTable(), line 1733

1733            t->tabwidth[0] = max_width;

where max_width=5632662 but tabwidth[0] is short. After assignment, tabwidth[0]=-3434

found by afl-fuzz

kcwu commented 8 years ago

5632662 comes from file.c 5016

5016                    width = REAL_WIDTH(i, h_env->limit - envs[h_env->envc].indent);

where i=-333330000, width=333330000*79/100=5632662.

I'm not familiar with html enough and don't know what is percentage larger than 100%. Maybe we should cap it to 100% ?

tats commented 8 years ago

Fixed, thank you.