这是indexloc提供的服务,不要输入任何密码
Self-registration is disabled due to spam issue (mail gorcunov@gmail.com or hpa@zytor.com to create an account)
Bug 3392760 - Stack use-after-scope in expand_mmac_params for indirect macro expansion
Summary: Stack use-after-scope in expand_mmac_params for indirect macro expansion
Status: OPEN
Alias: None
Product: NASM
Classification: Unclassified
Component: Assembler (show other bugs)
Version: 2.16.xx
Hardware: PC Linux
: Medium normal
Assignee: nobody
URL:
Depends on:
Blocks:
 
Reported: 2021-06-02 13:06 PDT by Marco
Modified: 2021-06-02 13:16 PDT (History)
5 users (show)

Obtained from: Built from git using configure
Generated by: ---
Bug category:
Breaks existing code: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Marco 2021-06-02 13:06:12 PDT
There's a stack use-after-scope issue in the `expand_mmac_params` function, when it tries to expand an indirect macro expansion.

The following minimized test case reproduces the issue:


```asm
%[
```

Run with:

```shell
$ nasm -f elf64 -g -FDWARF -o test.o file.asm
```

You can reproduce this crash by compiling with Address Sanitizer:

```
./configure --enable-sanitizer
```

Here's the Address Sanitizer output:

```
src/ce37e0123:1: error: label or instruction expected at start of line
src/ce37e0123:4: warning: unterminated %[ construct [-w+other]
=================================================================
==757284==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fff4dad6140 at pc 0x000000566e6f bp 0x7fff4dad6110 sp 0x7fff4dad6108
WRITE of size 8 at 0x7fff4dad6140 thread T0
    #0 0x566e6e in expand_mmac_params /home/markov/nasm/asm/preproc.c:5373:11
    #1 0x532fba in pp_tokline /home/markov/nasm/asm/preproc.c:7258:21
    #2 0x530674 in pp_getline /home/markov/nasm/asm/preproc.c:7328:17
    #3 0x4c05d9 in assemble_file /home/markov/nasm/asm/nasm.c:1722:24
    #4 0x4c05d9 in main /home/markov/nasm/asm/nasm.c:717:9
    #5 0x7f369a9a90b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
    #6 0x41c64d in _start (/home/markov/nasm/nasm+0x41c64d)

Address 0x7fff4dad6140 is located in stack of thread T0 at offset 32 in frame
    #0 0x5623bf in expand_mmac_params /home/markov/nasm/asm/preproc.c:5188

  This frame has 9 object(s):
    [32, 40) 'newlist.i488' (line 892) <== Memory access at offset 32 is inside this variable
    [64, 72) 'newlist.i' (line 915)
    [96, 104) 'newlist.i.i' (line 915)
    [128, 132) 'fst.i' (line 5136)
    [144, 148) 'lst.i' (line 5136)
    [160, 168) 'thead' (line 5189)
    [192, 200) 'ep' (line 5254)
    [224, 232) 'ep104' (line 5272)
    [256, 272) 't260' (line 5376)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope /home/markov/nasm/asm/preproc.c:5373:11 in expand_mmac_params
Shadow bytes around the buggy address:
  0x100069b52bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100069b52be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100069b52bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100069b52c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100069b52c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100069b52c20: 00 00 00 00 f1 f1 f1 f1[f8]f2 f2 f2 f8 f2 f2 f2
  0x100069b52c30: f8 f2 f2 f2 f8 f2 f8 f2 00 f2 f2 f2 f8 f2 f2 f2
  0x100069b52c40: f8 f2 f2 f2 f8 f8 f3 f3 00 00 00 00 00 00 00 00
  0x100069b52c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100069b52c60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100069b52c70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==757284==ABORTING

```

My understanding of the issue is as follows:

For this test file, when expand_mmac_params is called, it will enter the TOKEN_INDIRECT case[0]

The call to tokenize will return a NULL pointer, as the macro is incomplete (missing indirection and right bracket ])

Then, there's a call to dup_tlist(tt, &tail) right after (there's a comment asking why it is there, I don't know if it is safe to remove, but the problem arises then in that function)

The dup_tlist function[1] sets the tailpp pointer to the address of the newlist variable, which has an automatic duration tied to the scope of that function. In the normal case, for a non-empty list, this should be fine, as it will be modified while iterating the list.

But in this case, given that the token is NULL, the list is considered empty, so tailpp is never modified, and then, tailp is set with the value of tailpp, which still holds the address of the newlist variable.

Thus, when the function returns, the tail variable points to the newlist variable, which is out of scope.


[0]: https://github.com/netwide-assembler/nasm/blob/5368e45/asm/preproc.c#L5332-L5344
[1]: https://github.com/netwide-assembler/nasm/blob/5368e45/asm/preproc.c#L887-L908
Comment 1 Marco 2021-06-02 13:16:08 PDT
A quick fix could be to add something like this after the call to tokenize:

```c
if (tt == NULL) {
  text = NULL;
  change = false;
  break;
}
```

But I am not sure that doesn't break anything else in that function.