Re: WITH_ASAN= vs. vfork: ASAN can not deal with vfork in a reliable manor, so what to do to avoid vfork fairly generally, including for kyua?

2022-01-16 Thread Mark Millard
On 2022-Jan-15, at 15:25, Mark Millard  wrote:

> ASAN is documented to not be able to deal reliably with vfork use
> (inaccurate results, result mixing interpretations of the old
> and new contexts, and so on). [There may be other routines
> sufficiently analogous to vfork to have the same sorts of issues.
> I'll write only of vfork explicitly.]
> 
> It turns out that using SH_DISABLE_VFORK= with kyua runs
> helps avoid a bunch of junk failure ASAN reports that involve
> /bin/sh using vfork. So I've been recently using the likes of:
> 
> env SH_DISABLE_VFORK= ASAN_OPTIONS=detect_container_overflow=0 \
> kyua test -k /usr/tests/Kyuafile
> 
> in my kyua runs. But that need not cover all the vfork use in
> the kyua runs --or in general system operation.
> 
> 
> 
> There is more that could be done. For example . . .
> 
> contrib/llvm-project/compiler-rt/lib/asan/asan_interceptors.h
> currently has:
> 
> #if SANITIZER_LINUX &&\
>(defined(__arm__) || defined(__aarch64__) || defined(__i386__) || \
> defined(__x86_64__) || SANITIZER_RISCV64)
> # define ASAN_INTERCEPT_VFORK 1
> #else
> # define ASAN_INTERCEPT_VFORK 0
> #endif
> 
> The "# define ASAN_INTERCEPT_VFORK 1" causes interception to
> use fork for the vfork implementation, instead of an actual
> vfork.
> 
> It looks to me like asan_interceptors.h for FreeBSD should
> also be using:
> 
> # define ASAN_INTERCEPT_VFORK 1
> 
> but it currently is not.

Well, looking around there does not seem to be a FreeBSD-supporting
implementation to go with use of "# define ASAN_INTERCEPT_VFORK 1".
So there would be more to it than just causing the #define to happen.

Thus, there does not seem to be a quick way for me to see what would
result in the kyua run from more avoiding of vfork use (beyond what
use of SH_DISABLE_VFORK= did).

> There is even the possibility that a WITH_ASAN= buildworld could
> build a world that uses fork behavior for vfork and never does an
> actual vfork (by behavior). Basically restricting itself to things
> that fit with ASAN use.
> 
> 
> 
> I tracked this issue down via a report in a kyua run that
> referenced:
> 
> QUOTE
> ERROR: AddressSanitizer: stack-buffer-underflow on address 0x7fffa580 at 
> pc 0x000801e0f8ed bp 0x7fff9bf0 sp 0x7fff9be8
> WRITE of size 8 at 0x7fffa580 thread T0
> . . .
> Address 0x7fffa580 is located in stack of thread T0 at offset 0 in frame
>#0 0x7fffa5ef  ()
> 
>  This frame has 1 object(s):
>[32, 288) 'buf' (line 180)
> 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)
> END QUOTE
> 
> The "'buf' (line 180)" turned out to be from the declaration in
> bin/sh/exec.c 's tryexec :
> 
> static void
> tryexec(char *cmd, char **argv, char **envp)
> {
>int e, in;
>ssize_t n;
>char buf[256];
> 
>execve(cmd, argv, envp);
>e = errno;
>if (e == ENOEXEC) {
>INTOFF;
>in = open(cmd, O_RDONLY | O_NONBLOCK);
>if (in != -1) {
>n = pread(in, buf, sizeof buf, 0);
>close(in);
>if (n > 0 && isbinary(buf, n)) {
>errno = ENOEXEC;
>return;
>}
>}
>*argv = cmd;
>*--argv = __DECONST(char *, _PATH_BSHELL);
>execve(_PATH_BSHELL, argv, envp);
>}
>errno = e;
> }
> 
> So I could finally see/validate that an example
> stack-buffer-* report was tied to vfork handling
> (analyzing other code related to the tryexec use).
> 
> The report happens well after tryexec did its
> execve. The information about buf is just old,
> no longer accurate information, leading to a
> false report:
> 
> 
> ==87961==ERROR: AddressSanitizer: stack-buffer-underflow on address 
> 0x7fffa580 at pc 0x000801e0f8ed bp 0x7fff9bf0 sp 0x7fff9be8
> WRITE of size 8 at 0x7fffa580 thread T0
>#0 0x801e0f8ec in bintime2timespec 
> /usr/obj/BUILDs/main-amd64-nodbg-clang-alt/usr/main-src/amd64.amd64/tmp/usr/include/sys/time.h:285:14
>#1 0x801e0f8ec in __vdso_clock_gettime 
> /usr/main-src/lib/libc/sys/__vdso_gettimeofday.c:195:2
>#2 0x801e0e1a0 in clock_gettime 
> /usr/main-src/lib/libc/sys/clock_gettime.c:48:11
>#3 0x10d54da in clock_gettime 
> /usr/main-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc:2189:13
>#4 0x11234f5 in __sanitizer::MonotonicNanoTime() 
> /usr/main-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp:860:3
>#5 0x10ba02c in 
> __sanitizer::SizeClassAllocator64<__asan::AP64<__sanitizer::LocalAddressSpaceView>
>  >::PopulateFreeArray(__sanitizer::AllocatorStats*, unsigned long, 
> __sanitizer::SizeClassAllocator
> 64<__as

UBSAN report from kyua run in WITH_UBSAN= based world (via chroot): /bin/sh 's waitcmdloop does NULL+0 undefined behavior

2022-01-16 Thread Mark Millard
# /bin/sh /usr/tests/bin/sh/builtins/wait6.0
/usr/main-src/bin/sh/jobs.c:590:35: runtime error: applying zero offset to null 
pointer
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior 
/usr/main-src/bin/sh/jobs.c:590:35 in 
/usr/main-src/bin/sh/jobs.c:601:22: runtime error: applying zero offset to null 
pointer
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior 
/usr/main-src/bin/sh/jobs.c:601:22 in 

So:

# lldb /bin/sh /usr/tests/bin/sh/builtins/wait6.0
(lldb) target create "/bin/sh"
Current executable set to '/bin/sh' (x86_64).
(lldb) settings set -- target.run-args  "/usr/tests/bin/sh/builtins/wait6.0"
(lldb) run
Process 66125 launched: '/bin/sh' (x86_64)
Process 66125 stopped
* thread #1, name = 'sh', stop reason = Nullptr with offset
frame #0: 0x01135850 sh`::__ubsan_on_report() at 
ubsan_monitor.cpp:39
   36   }
   37   
   38   SANITIZER_WEAK_DEFAULT_IMPL
-> 39   void __ubsan::__ubsan_on_report(void) {}
   40   
   41   void __ubsan::__ubsan_get_current_report_data(const char **OutIssueKind,
   42 const char **OutMessage,
(lldb) bt
* thread #1, name = 'sh', stop reason = Nullptr with offset
  * frame #0: 0x01135850 sh`::__ubsan_on_report() at 
ubsan_monitor.cpp:39
frame #1: 0x01130011 
sh`__ubsan::Diag::~Diag(this=0x7fffcc60) at ubsan_diag.cpp:354:29
frame #2: 0x01134f44 
sh`handlePointerOverflowImpl(Data=, Base=, 
Result=, Opts=(FromUnrecoverableHandler = false, pc = 18263566, bp 
= 140737488343328)) at ubsan_diag.h:0:21
frame #3: 0x01134a7a 
sh`::__ubsan_handle_pointer_overflow(Data=, Base=, 
Result=) at ubsan_handlers.cpp:815:3
frame #4: 0x0116ae0e sh`waitcmdloop(job=0x) at 
jobs.c:590:35
frame #5: 0x0114528a sh`evalcommand(cmd=, flags=0, 
backcmd=0x) at eval.c:1107:16
frame #6: 0x0113eeb8 sh`evaltree(n=0x615000d8, 
flags=) at eval.c:289:4
frame #7: 0x0117a317 sh`cmdloop(top=) at main.c:228:4
frame #8: 0x01179789 sh`main(argc=2, argv=) at 
main.c:175:3
frame #9: 0x010b35dd sh`_start(ap=, 
cleanup=) at crt1_c.c:73:7
(lldb) thread info -s
thread #1: tid = 101020, 0x01135850 sh`::__ubsan_on_report() at 
ubsan_monitor.cpp:39, name = 'sh', stop reason = Nullptr with offset

{
  "col": 35,
  "description": "nullptr-with-offset",
  "filename": "/usr/main-src/bin/sh/jobs.c",
  "instrumentation_class": "UndefinedBehaviorSanitizer",
  "line": 590,
  "memory_address": 0,
  "summary": "Applying zero offset to null pointer",
  "tid": 101020,
  "trace": []
}
(lldb) up 4
frame #4: 0x0116ae0e sh`waitcmdloop(job=0x) at 
jobs.c:590:35
   587  return retval;
   588  }
   589  } else {
-> 590  for (jp = jobtab ; jp < jobtab + njobs; jp++)
   591  if (jp->used && jp->state == JOBDONE) {
   592  if (! iflag || ! jp->changed)
   593  freejob(jp);
(lldb) c
Process 66125 resuming
/usr/main-src/bin/sh/jobs.c:590:35: runtime error: applying zero offset to null 
pointer
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior 
/usr/main-src/bin/sh/jobs.c:590:35 in 
Process 66125 stopped
* thread #1, name = 'sh', stop reason = Nullptr with offset
frame #0: 0x01135850 sh`::__ubsan_on_report() at 
ubsan_monitor.cpp:39
   36   }
   37   
   38   SANITIZER_WEAK_DEFAULT_IMPL
-> 39   void __ubsan::__ubsan_on_report(void) {}
   40   
   41   void __ubsan::__ubsan_get_current_report_data(const char **OutIssueKind,
   42 const char **OutMessage,
(lldb) bt
* thread #1, name = 'sh', stop reason = Nullptr with offset
  * frame #0: 0x01135850 sh`::__ubsan_on_report() at 
ubsan_monitor.cpp:39
frame #1: 0x01130011 
sh`__ubsan::Diag::~Diag(this=0x7fffcc60) at ubsan_diag.cpp:354:29
frame #2: 0x01134f44 
sh`handlePointerOverflowImpl(Data=, Base=, 
Result=, Opts=(FromUnrecoverableHandler = false, pc = 1826, bp 
= 140737488343328)) at ubsan_diag.h:0:21
frame #3: 0x01134a7a 
sh`::__ubsan_handle_pointer_overflow(Data=, Base=, 
Result=) at ubsan_handlers.cpp:815:3
frame #4: 0x0116b17c sh`waitcmdloop(job=0x) at 
jobs.c:601:22
frame #5: 0x0114528a sh`evalcommand(cmd=, flags=0, 
backcmd=0x) at eval.c:1107:16
frame #6: 0x0113eeb8 sh`evaltree(n=0x615000d8, 
flags=) at eval.c:289:4
frame #7: 0x0117a317 sh`cmdloop(top=) at main.c:228:4
frame #8: 0x01179789 sh`main(argc=2, argv=) at 
main.c:175:3
frame #9: 0x010b35dd sh`_start(ap=, 
cleanup=) at crt1_c.c:73:7
(lldb) thread info -s
thread #1: tid = 101020, 0x01135850 sh`::__ubsan_on_report() at 
ubsan