nasm-2.14-segment-fault

There exsits one null pointer dereference in function bsii at nasmlib/bsi.c:68 in nasm-2.14rc15. which can cause a segment fault.

To reproduce:

1
2
./configure && make
./nasm -f elf poc

gdb output:
backtrace:

1
2
3
4
5
6
7
#0  __strcasecmp_l_avx () at ../sysdeps/x86_64/multiarch/strcmp-sse42.S:198
#1 0x00005555555f2291 in bsii (string=0x0, array=0x5555558de220 <size_names>, size=7) at nasmlib/bsi.c:68 <-------null str
#2 0x00005555555bc9e0 in parse_size (str=0x0) at asm/preproc.c:2214 <-------null str
#3 0x00005555555bd310 in do_directive (tline=0x7ffff7fda2d0, output=0x7fffffffd6b8) at asm/preproc.c:2471
#4 0x00005555555c49a4 in pp_getline () at asm/preproc.c:5210
#5 0x00005555555a86ae in assemble_file (fname=0x5555558f3d60 "/dev/stdin", depend_ptr=0x0) at asm/nasm.c:1435
#6 0x00005555555a65e5 in main (argc=6, argv=0x7fffffffda88) at asm/nasm.c:566

source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// in nasmlib/bsi.c      len = 68
int bsii(const char *string, const char **array, int size)
{
int i = -1, j = size; /* always, i < index < j */
while (j - i >= 2) {
int k = (i + j) / 2;
int l = nasm_stricmp(string, array[k]); <-----crash null str
if (l < 0) /* it's in the first half */
j = k;
...

// in asm/preproc.c len = 2214
static int parse_size(const char *str) {
static const char *size_names[] =
{ "byte", "dword", "oword", "qword", "tword", "word", "yword" };
static const int sizes[] =
{ 0, 1, 4, 16, 8, 10, 2, 32 };

return sizes[bsii(str, size_names, ARRAY_SIZE(size_names))+1]; <--- crash null str
}

poc

1
2
%define dword
%arg val:dword

根据asm语义,poc的语义将dword定义为null,而在第二句中,将val定义为dword类型,在此出现了语义的重复,导致了在解析dword的过程中,将dword理解为null

1
2
3
4
5
// at asm/preproc.c:2471
2468 /* Allow macro expansion of type parameter */
2469 tt = tokenize(tline->text);
► 2470 tt = expand_smacro(tt); <---tt is null pointer
2471 size = parse_size(tt->text);
1
2
3
4
5
6
7
8
9
pwndbg> p *head
$150 = {
next = 0x0,
name = 0x555555934e70 "dword",
casesense = true,
in_progress = false,
nparam = 0,
expansion = 0x0
}

head->expansion == null

问题应该出现在hash_findix()函数中,因为define dword之后没有对应的字符串,hash_findix()函数将expansion赋值为空,导致函数返回时返回了空的thead

preproc.c

poc导致程序crash在于:

1
2
3
4
5
6
7
8
9
10
11
12
13
//preproc.c at line 4230
head = (SMacro *)hash_findix(&smacros, mname);

//preproc.c at line 4243
list_for_each(m, head)
if (!mstrcmp(m->name, mname, m->casesense))
break;

//preproc.c at line 4256
if (!m->expansion) {
...
tline = delete_Token(tline);
continue;

因为在正确的宏定义当中,%define ABC abc将abc定义为ABC。但是,由于poc中%define dword没有定义值,所以,在hash_findix()中取的的值中head->expansion为空,在if条件分支里,进行了删除操作,之后继续执行。因此在此处返回值变为null。

hash_findix()

hash_findix(struct hash_table *hash, const char *str)
根据输入的字符串取一个(SMacro *)类型的值。即取出宏定义是对应的宏。该值会包含一些宏的信息,但是由于在宏定义是没有宏的值进行定义,所以expansion为空。