diff options
| author | Mark Johnston <markjdb@gmail.com> | 2019-11-05 14:54:47 -0500 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2019-11-08 10:51:32 +0100 |
| commit | 5e1ad02b1770214d9503070b52c4eb7bdd6c6cff (patch) | |
| tree | 9f461da0427584ff20b2504ac623af478c1c7bd0 /pkg/csource | |
| parent | 1e35461e020a8b43c7b2d79ee64eb4068a9f0481 (diff) | |
pkg/csource: Force promotion of 64-bit constant values
Constant 64-bit arguments to the variadic syscall(2) must have their
width specified explicitly. In practice this is not necessary most of
the time, but on amd64/freebsd with clang the compiler can and does
store the constant 32-bit value to the stack, leaving garbage in the
upper 32 bits.
This makes C reproducers somewhat uglier, but I see no other solution.
Diffstat (limited to 'pkg/csource')
| -rw-r--r-- | pkg/csource/csource.go | 30 |
1 files changed, 26 insertions, 4 deletions
diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index a1f3727a7..9ca3b29e7 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -233,7 +233,7 @@ func (ctx *context) emitCall(w *bytes.Buffer, call prog.ExecCall, ci int, haveCo if arg.Format != prog.FormatNative && arg.Format != prog.FormatBigEndian { panic("sring format in syscall argument") } - fmt.Fprintf(w, "%v", ctx.constArgToStr(arg, true)) + fmt.Fprintf(w, "%v", ctx.constArgToStr(arg, true, native)) case prog.ExecArgResult: if arg.Format != prog.FormatNative && arg.Format != prog.FormatBigEndian { panic("sring format in syscall argument") @@ -312,7 +312,7 @@ func (ctx *context) copyin(w *bytes.Buffer, csumSeq *int, copyin prog.ExecCopyin switch arg := copyin.Arg.(type) { case prog.ExecArgConst: if arg.BitfieldOffset == 0 && arg.BitfieldLength == 0 { - ctx.copyinVal(w, copyin.Addr, arg.Size, ctx.constArgToStr(arg, true), arg.Format) + ctx.copyinVal(w, copyin.Addr, arg.Size, ctx.constArgToStr(arg, true, false), arg.Format) } else { if arg.Format != prog.FormatNative && arg.Format != prog.FormatBigEndian { panic("bitfield+string format") @@ -322,7 +322,7 @@ func (ctx *context) copyin(w *bytes.Buffer, csumSeq *int, copyin prog.ExecCopyin htobe = fmt.Sprintf("htobe%v", arg.Size*8) } fmt.Fprintf(w, "\tNONFAILING(STORE_BY_BITMASK(uint%v, %v, 0x%x, %v, %v, %v));\n", - arg.Size*8, htobe, copyin.Addr, ctx.constArgToStr(arg, false), + arg.Size*8, htobe, copyin.Addr, ctx.constArgToStr(arg, false, false), arg.BitfieldOffset, arg.BitfieldLength) } case prog.ExecArgResult: @@ -397,7 +397,7 @@ func (ctx *context) copyout(w *bytes.Buffer, call prog.ExecCall, resCopyout bool } } -func (ctx *context) constArgToStr(arg prog.ExecArgConst, handleBigEndian bool) string { +func (ctx *context) constArgToStr(arg prog.ExecArgConst, handleBigEndian, native bool) string { mask := (uint64(1) << (arg.Size * 8)) - 1 v := arg.Value & mask val := fmt.Sprintf("%v", v) @@ -406,6 +406,28 @@ func (ctx *context) constArgToStr(arg prog.ExecArgConst, handleBigEndian bool) s } else if v >= 10 { val = fmt.Sprintf("0x%x", v) } + if native && arg.Size == 8 { + // syscall() is variadic, so constant arguments must be explicitly + // promoted. Otherwise the compiler is free to leave garbage in the + // upper 32 bits of the argument value. In practice this can happen + // on amd64 with arguments that are passed on the stack, i.e., + // arguments beyond the first six. For example, on freebsd/amd64, + // syscall(SYS_mmap, ..., 0) causes clang to emit a 32-bit store of + // 0 to the stack, but the kernel expects a 64-bit value. + // + // syzkaller's argument type representations do not always match + // the OS ABI. For instance, "flags" is always 64 bits wide on 64-bit + // platforms, but is a 32-bit value ("unsigned int" or so) in many + // cases. Thus, we assume here that passing a 64-bit argument where + // a 32-bit argument is expected won't break anything. On amd64 + // this should be fine: arguments are passed in 64-bit registers or + // at 64 bit-aligned addresses on the stack. + if ctx.target.PtrSize == 4 { + val += "ull" + } else { + val += "ul" + } + } if ctx.opts.Procs > 1 && arg.PidStride != 0 { val += fmt.Sprintf(" + procid*%v", arg.PidStride) } |
