csource: add EnableTun option

This commit is contained in:
Andrey Konovalov 2017-05-17 20:20:23 +02:00
parent 1ab96df885
commit 7d7c9c550f
7 changed files with 154 additions and 110 deletions

View File

@ -167,10 +167,6 @@ static void install_segv_handler()
*(type*)(addr) = new_val; \
}
#if defined(__NR_syz_emit_ethernet) || defined(__NR_syz_extract_tcp_res)
#define SYZ_TUN_ENABLE
#endif
#ifdef SYZ_TUN_ENABLE
static void vsnprintf_check(char* str, size_t size, const char* format, va_list args)
{
@ -279,6 +275,17 @@ static void setup_tun(uint64_t pid, bool enable_tun)
initialize_tun(pid);
}
int read_tun(char* data, int size)
{
int rv = read(tunfd, data, size);
if (rv < 0) {
if (errno == EAGAIN)
return -1;
fail("tun: read failed with %d, errno: %d", rv, errno);
}
return rv;
}
void debug_dump_data(const char* data, int length)
{
int i;
@ -292,7 +299,7 @@ void debug_dump_data(const char* data, int length)
}
#endif
#if defined(__NR_syz_emit_ethernet) || defined(__NR_syz_test)
#if (defined(__NR_syz_emit_ethernet) && defined(SYZ_TUN_ENABLE)) || defined(__NR_syz_test)
struct csum_inet {
uint32_t acc;
};
@ -324,7 +331,7 @@ uint16_t csum_inet_digest(struct csum_inet* csum)
}
#endif
#ifdef __NR_syz_emit_ethernet
#if defined(__NR_syz_emit_ethernet) && defined(SYZ_TUN_ENABLE)
static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
{
@ -338,7 +345,16 @@ static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
}
#endif
#ifdef __NR_syz_extract_tcp_res
#if (defined(SYZ_EXECUTOR) || defined(SYZ_REPEAT)) && defined(SYZ_TUN_ENABLE)
void flush_tun()
{
char data[SYZ_TUN_MAX_PACKET_SIZE];
while (read_tun(&data[0], sizeof(data)) != -1)
;
}
#endif
#if defined(__NR_syz_extract_tcp_res) && defined(SYZ_TUN_ENABLE)
struct ipv6hdr {
__u8 priority : 4,
version : 4;
@ -357,17 +373,6 @@ struct tcp_resources {
int32_t ack;
};
int read_tun(char* data, int size)
{
int rv = read(tunfd, data, size);
if (rv < 0) {
if (errno == EAGAIN)
return -1;
fail("tun: read failed with %d, errno: %d", rv, errno);
}
return rv;
}
static uintptr_t syz_extract_tcp_res(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
@ -418,15 +423,6 @@ static uintptr_t syz_extract_tcp_res(uintptr_t a0, uintptr_t a1, uintptr_t a2)
}
#endif
#if defined(SYZ_TUN_ENABLE) && (defined(SYZ_EXECUTOR) || defined(SYZ_REPEAT))
void flush_tun()
{
char data[SYZ_TUN_MAX_PACKET_SIZE];
while (read_tun(&data[0], sizeof(data)) != -1)
;
}
#endif
#ifdef __NR_syz_open_dev
static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
@ -1505,13 +1501,21 @@ static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a
case __NR_syz_fuseblk_mount:
return syz_fuseblk_mount(a0, a1, a2, a3, a4, a5, a6, a7);
#endif
#ifdef __NR_syz_emit_ethernet
#if defined(__NR_syz_emit_ethernet)
case __NR_syz_emit_ethernet:
#if defined(SYZ_TUN_ENABLE)
return syz_emit_ethernet(a0, a1);
#else
return 0;
#endif
#ifdef __NR_syz_extract_tcp_res
#endif
#if defined(__NR_syz_extract_tcp_res)
case __NR_syz_extract_tcp_res:
#if defined(SYZ_TUN_ENABLE)
return syz_extract_tcp_res(a0, a1, a2);
#else
return 0;
#endif
#endif
#ifdef __NR_syz_kvm_setup_cpu
case __NR_syz_kvm_setup_cpu:

View File

@ -22,15 +22,22 @@ import (
)
type Options struct {
Threaded bool
Collide bool
Repeat bool
Procs int
Sandbox string
Threaded bool
Collide bool
Repeat bool
Procs int
Sandbox string
Fault bool // inject fault into FaultCall/FaultNth
FaultCall int
FaultNth int
Repro bool // generate code for use with repro package
// These options allow for a more fine-tuned control over the generated C code.
EnableTun bool
// Generate code for use with repro package to prints log messages,
// which allows to distinguish between a hang and an absent crash.
Repro bool
}
func Write(p *prog.Prog, opts Options) ([]byte, error) {
@ -53,14 +60,6 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
}
fmt.Fprintf(w, "\n")
enableTun := "false"
if _, ok := handled["syz_emit_ethernet"]; ok {
enableTun = "true"
}
if _, ok := handled["syz_extract_tcp_res"]; ok {
enableTun = "true"
}
hdr, err := preprocessCommonHeader(opts, handled)
if err != nil {
return nil, err
@ -76,7 +75,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
fmt.Fprint(w, "int main()\n{\n")
fmt.Fprintf(w, "\tsetup_main_process();\n")
fmt.Fprintf(w, "\tint pid = do_sandbox_%v(0, %v);\n", opts.Sandbox, enableTun)
fmt.Fprintf(w, "\tint pid = do_sandbox_%v(0, %v);\n", opts.Sandbox, opts.EnableTun)
fmt.Fprint(w, "\tint status = 0;\n")
fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n")
fmt.Fprint(w, "\treturn 0;\n}\n")
@ -85,7 +84,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
if opts.Procs <= 1 {
fmt.Fprint(w, "int main()\n{\n")
fmt.Fprintf(w, "\tsetup_main_process();\n")
fmt.Fprintf(w, "\tint pid = do_sandbox_%v(0, %v);\n", opts.Sandbox, enableTun)
fmt.Fprintf(w, "\tint pid = do_sandbox_%v(0, %v);\n", opts.Sandbox, opts.EnableTun)
fmt.Fprint(w, "\tint status = 0;\n")
fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n")
fmt.Fprint(w, "\treturn 0;\n}\n")
@ -95,7 +94,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", opts.Procs)
fmt.Fprint(w, "\t\tif (fork() == 0) {\n")
fmt.Fprintf(w, "\t\t\tsetup_main_process();\n")
fmt.Fprintf(w, "\t\t\tint pid = do_sandbox_%v(i, %v);\n", opts.Sandbox, enableTun)
fmt.Fprintf(w, "\t\t\tint pid = do_sandbox_%v(i, %v);\n", opts.Sandbox, opts.EnableTun)
fmt.Fprint(w, "\t\t\tint status = 0;\n")
fmt.Fprint(w, "\t\t\twhile (waitpid(pid, &status, __WALL) != pid) {}\n")
fmt.Fprint(w, "\t\t\treturn 0;\n")
@ -322,6 +321,9 @@ func preprocessCommonHeader(opts Options, handled map[string]int) (string, error
if opts.Fault {
defines = append(defines, "SYZ_FAULT_INJECTION")
}
if opts.EnableTun {
defines = append(defines, "SYZ_TUN_ENABLE")
}
for name, _ := range handled {
defines = append(defines, "__NR_"+name)
}

View File

@ -32,20 +32,22 @@ func allOptionsPermutations() []Options {
for _, opt.Threaded = range []bool{false, true} {
for _, opt.Collide = range []bool{false, true} {
for _, opt.Repeat = range []bool{false, true} {
for _, opt.Repro = range []bool{false, true} {
for _, opt.Procs = range []int{1, 4} {
for _, opt.Sandbox = range []string{"none", "setuid", "namespace"} {
for _, opt.Fault = range []bool{false, true} {
if opt.Collide && !opt.Threaded {
continue
for _, opt.Procs = range []int{1, 4} {
for _, opt.Sandbox = range []string{"none", "setuid", "namespace"} {
for _, opt.Repro = range []bool{false, true} {
for _, opt.EnableTun = range []bool{false, true} {
for _, opt.Fault = range []bool{false, true} {
if opt.Collide && !opt.Threaded {
continue
}
if !opt.Repeat && opt.Procs != 1 {
continue
}
if testing.Short() && opt.Procs != 1 {
continue
}
options = append(options, opt)
}
if !opt.Repeat && opt.Procs != 1 {
continue
}
if testing.Short() && opt.Procs != 1 {
continue
}
options = append(options, opt)
}
}
}

View File

@ -189,10 +189,6 @@ static void install_segv_handler()
*(type*)(addr) = new_val; \
}
#if defined(__NR_syz_emit_ethernet) || defined(__NR_syz_extract_tcp_res)
#define SYZ_TUN_ENABLE
#endif
#ifdef SYZ_TUN_ENABLE
static void vsnprintf_check(char* str, size_t size, const char* format, va_list args)
{
@ -307,6 +303,17 @@ static void setup_tun(uint64_t pid, bool enable_tun)
initialize_tun(pid);
}
int read_tun(char* data, int size)
{
int rv = read(tunfd, data, size);
if (rv < 0) {
if (errno == EAGAIN)
return -1;
fail("tun: read failed with %d, errno: %d", rv, errno);
}
return rv;
}
void debug_dump_data(const char* data, int length)
{
int i;
@ -320,7 +327,7 @@ void debug_dump_data(const char* data, int length)
}
#endif // SYZ_TUN_ENABLE
#if defined(__NR_syz_emit_ethernet) || defined(__NR_syz_test)
#if (defined(__NR_syz_emit_ethernet) && defined(SYZ_TUN_ENABLE)) || defined(__NR_syz_test)
struct csum_inet {
uint32_t acc;
};
@ -352,7 +359,7 @@ uint16_t csum_inet_digest(struct csum_inet* csum)
}
#endif
#ifdef __NR_syz_emit_ethernet
#if defined(__NR_syz_emit_ethernet) && defined(SYZ_TUN_ENABLE)
static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
{
// syz_emit_ethernet(len len[packet], packet ptr[in, eth_packet])
@ -367,7 +374,16 @@ static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
}
#endif
#ifdef __NR_syz_extract_tcp_res
#if (defined(SYZ_EXECUTOR) || defined(SYZ_REPEAT)) && defined(SYZ_TUN_ENABLE)
void flush_tun()
{
char data[SYZ_TUN_MAX_PACKET_SIZE];
while (read_tun(&data[0], sizeof(data)) != -1)
;
}
#endif
#if defined(__NR_syz_extract_tcp_res) && defined(SYZ_TUN_ENABLE)
// Can't include <linux/ipv6.h>, since it causes
// conflicts due to some structs redefinition.
struct ipv6hdr {
@ -388,17 +404,6 @@ struct tcp_resources {
int32_t ack;
};
int read_tun(char* data, int size)
{
int rv = read(tunfd, data, size);
if (rv < 0) {
if (errno == EAGAIN)
return -1;
fail("tun: read failed with %d, errno: %d", rv, errno);
}
return rv;
}
static uintptr_t syz_extract_tcp_res(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
// syz_extract_tcp_res(res ptr[out, tcp_resources], seq_inc int32, ack_inc int32)
@ -451,15 +456,6 @@ static uintptr_t syz_extract_tcp_res(uintptr_t a0, uintptr_t a1, uintptr_t a2)
}
#endif
#if defined(SYZ_TUN_ENABLE) && (defined(SYZ_EXECUTOR) || defined(SYZ_REPEAT))
void flush_tun()
{
char data[SYZ_TUN_MAX_PACKET_SIZE];
while (read_tun(&data[0], sizeof(data)) != -1)
;
}
#endif
#ifdef __NR_syz_open_dev
static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
@ -597,14 +593,22 @@ static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a
case __NR_syz_fuseblk_mount:
return syz_fuseblk_mount(a0, a1, a2, a3, a4, a5, a6, a7);
#endif
#ifdef __NR_syz_emit_ethernet
#if defined(__NR_syz_emit_ethernet)
case __NR_syz_emit_ethernet:
#if defined(SYZ_TUN_ENABLE)
return syz_emit_ethernet(a0, a1);
#endif
#ifdef __NR_syz_extract_tcp_res
#else
return 0;
#endif // defined(SYZ_TUN_ENABLE)
#endif // defined(__NR_syz_emit_ethernet)
#if defined(__NR_syz_extract_tcp_res)
case __NR_syz_extract_tcp_res:
#if defined(SYZ_TUN_ENABLE)
return syz_extract_tcp_res(a0, a1, a2);
#endif
#else
return 0;
#endif // defined(SYZ_TUN_ENABLE)
#endif // defined(__NR_syz_extract_tcp_res)
#ifdef __NR_syz_kvm_setup_cpu
case __NR_syz_kvm_setup_cpu:
return syz_kvm_setup_cpu(a0, a1, a2, a3, a4, a5, a6, a7);

View File

@ -31,6 +31,7 @@
#include "syscalls.h"
#define SYZ_EXECUTOR
#define SYZ_TUN_ENABLE
#include "common.h"
#define KCOV_INIT_TRACE _IOR('c', 1, unsigned long long)

View File

@ -151,12 +151,13 @@ func (ctx *context) repro(entries []*prog.LogEntry, crashStart int) (*Result, er
}
Logf(2, "reproducing crash '%v': suspecting %v programs", ctx.crashDesc, len(suspected))
opts := csource.Options{
Threaded: true,
Collide: true,
Repeat: true,
Procs: ctx.cfg.Procs,
Sandbox: ctx.cfg.Sandbox,
Repro: true,
Threaded: true,
Collide: true,
Repeat: true,
Procs: ctx.cfg.Procs,
Sandbox: ctx.cfg.Sandbox,
EnableTun: true,
Repro: true,
}
// Execute the suspected programs.
// We first try to execute each program for 10 seconds, that should detect simple crashes
@ -264,24 +265,29 @@ func (ctx *context) repro(entries []*prog.LogEntry, crashStart int) (*Result, er
}
}
src, err := csource.Write(res.Prog, res.Opts)
if err != nil {
return res, err
}
srcf, err := fileutil.WriteTempFile(src)
if err != nil {
return res, err
}
bin, err := csource.Build("c", srcf)
if err != nil {
return res, err
}
defer os.Remove(bin)
crashed, err = ctx.testBin(bin, duration)
// Try triggering crash with a C reproducer.
crashed, err = ctx.testCProg(res.Prog, duration, res.Opts)
if err != nil {
return res, err
}
res.CRepro = crashed
if !crashed {
return res, nil
}
// Try to simplify the C reproducer.
if res.Opts.EnableTun {
opts = res.Opts
opts.EnableTun = false
crashed, err := ctx.testCProg(res.Prog, duration, opts)
if err != nil {
return res, err
}
if crashed {
res.Opts = opts
}
}
return res, nil
}
@ -317,6 +323,29 @@ func (ctx *context) testProg(p *prog.Prog, duration time.Duration, opts csource.
return ctx.testImpl(inst.Instance, command, duration)
}
func (ctx *context) testCProg(p *prog.Prog, duration time.Duration, opts csource.Options) (crashed bool, err error) {
src, err := csource.Write(p, opts)
if err != nil {
return false, err
}
srcf, err := fileutil.WriteTempFile(src)
if err != nil {
return false, err
}
bin, err := csource.Build("c", srcf)
if err != nil {
return false, err
}
defer os.Remove(bin)
Logf(2, "reproducing crash '%v': testing compiled C program (duration=%v, %+v): %s",
ctx.crashDesc, duration, opts, p)
crashed, err = ctx.testBin(bin, duration)
if err != nil {
return false, err
}
return crashed, nil
}
func (ctx *context) testBin(bin string, duration time.Duration) (crashed bool, err error) {
inst := <-ctx.instances
if inst == nil {

View File

@ -22,6 +22,7 @@ var (
flagProg = flag.String("prog", "", "file with program to convert (required)")
flagFaultCall = flag.Int("fault_call", -1, "inject fault into this call (0-based)")
flagFaultNth = flag.Int("fault_nth", 0, "inject fault on n-th operation (0-based)")
flagEnableTun = flag.Bool("tun", false, "set up TUN/TAP interface")
)
func main() {
@ -49,6 +50,7 @@ func main() {
Fault: *flagFaultCall >= 0,
FaultCall: *flagFaultCall,
FaultNth: *flagFaultNth,
EnableTun: *flagEnableTun,
Repro: false,
}
src, err := csource.Write(p, opts)