vm/isolated: update isolated vm

* vm/isolated: update isolated vm

Old isolated.go cannot hard reset the target device when the target device is stuck,
because it used SSH command to reboot.
New isolated.go can reboot the target device using USB hub,
so it can reboot the device when its kernel is crashed during fuzzing.
It also doesn't require 'CGO' like odroid.go

* vm/isolated: set default Host, comment modification

* vm/isolated: restore ssh reboot in repair()

In the previous commit, ssh reboot is removed.
but it should be remained, so this commit restore the ssh reboot.
Now, repair() func can reboot the target using ssh or /sys/bus/usb/devices/.../authorized/

* vm/isolated: update USBdev rebooting method and etc, ...
- change reboot method from using /bin/sh to file method
- change USBDevNum to array type
- restore waiting time when rebooting

* vm/isolated: update USBdev rebooting method and etc, ...
- change reboot method from using '/bin/sh' to file i/o
- change USBDevNum to array type
- restore waiting time when rebooting

* vm/isolated: update USBdev rebooting method and etc, ...
- change reboot method from using '/bin/sh' to file i/o
- change USBDevNum to array type
- restore waiting time when rebooting

* vm/isolated: some fixes based on feedback
- change variable name: USBDevNum -> USBDevNums, USBAuth -> usbAuth
- check whether USBDevNums is empty in ctor(), repair()
- move usbAuth declaration from Create() to repair()

* vm/isolated: remove empty line

* vm/isolated: fix some conditions

* vm/isolated: change comment, add validate length of USBDevNums

* vm/isolated: check whether the len(USBDevNums) and len(Targets) is same

* vm/isolated: change repair() func based on review
- wait 30*time.Minute even if TargetReboot is not set.
- reduce/combine logs
- e -> err

* vm/isolated: In repair(), print error log and return error when ssh is failed
This commit is contained in:
Jonghyuk Song 2020-01-08 17:28:19 +09:00 committed by Dmitry Vyukov
parent 6738e0b30b
commit ddc3e85997

64
vm/isolated/isolated.go Normal file → Executable file
View File

@ -24,9 +24,11 @@ func init() {
}
type Config struct {
Targets []string `json:"targets"` // target machines: (hostname|ip)(:port)?
TargetDir string `json:"target_dir"` // directory to copy/run on target
TargetReboot bool `json:"target_reboot"` // reboot target on repair
Host string `json:"host"` // host ip addr
Targets []string `json:"targets"` // target machines: (hostname|ip)(:port)?
TargetDir string `json:"target_dir"` // directory to copy/run on target
TargetReboot bool `json:"target_reboot"` // reboot target on repair
USBDevNums []string `json:"usb_device_num"` // /sys/bus/usb/devices/
}
type Pool struct {
@ -39,6 +41,7 @@ type instance struct {
os string
targetAddr string
targetPort int
index int
closed chan bool
debug bool
sshUser string
@ -51,6 +54,9 @@ func ctor(env *vmimpl.Env) (vmimpl.Pool, error) {
if err := config.LoadData(env.Config, cfg); err != nil {
return nil, err
}
if cfg.Host == "" {
cfg.Host = "127.0.0.1"
}
if len(cfg.Targets) == 0 {
return nil, fmt.Errorf("config param targets is empty")
}
@ -62,9 +68,17 @@ func ctor(env *vmimpl.Env) (vmimpl.Pool, error) {
return nil, fmt.Errorf("bad target %q: %v", target, err)
}
}
if len(cfg.USBDevNums) > 0 {
if len(cfg.USBDevNums) != len(cfg.Targets) {
return nil, fmt.Errorf("the number of Targets and the number of USBDevNums should be same")
}
}
if env.Debug && len(cfg.Targets) > 1 {
log.Logf(0, "limiting number of targets from %v to 1 in debug mode", len(cfg.Targets))
cfg.Targets = cfg.Targets[:1]
if len(cfg.USBDevNums) > 1 {
cfg.USBDevNums = cfg.USBDevNums[:1]
}
}
pool := &Pool{
cfg: cfg,
@ -84,6 +98,7 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) {
os: pool.env.OS,
targetAddr: targetAddr,
targetPort: targetPort,
index: index,
closed: make(chan bool),
debug: pool.env.Debug,
sshUser: pool.env.SSHUser,
@ -99,6 +114,9 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) {
return nil, err
}
// Remount to writable.
inst.ssh("mount -o remount,rw /")
// Create working dir if doesn't exist.
inst.ssh("mkdir -p '" + inst.cfg.TargetDir + "'")
@ -117,7 +135,7 @@ func (inst *instance) Forward(port int) (string, error) {
return "", fmt.Errorf("isolated: Forward port is zero")
}
inst.forwardPort = port
return fmt.Sprintf("127.0.0.1:%v", port), nil
return fmt.Sprintf(inst.cfg.Host+":%v", port), nil
}
func (inst *instance) ssh(command string) error {
@ -175,26 +193,36 @@ func (inst *instance) repair() error {
log.Logf(2, "isolated: trying to ssh")
if err := inst.waitForSSH(30 * time.Minute); err == nil {
if inst.cfg.TargetReboot {
log.Logf(2, "isolated: trying to reboot")
inst.ssh("reboot") // reboot will return an error, ignore it
if err := inst.waitForReboot(5 * 60); err != nil {
log.Logf(2, "isolated: machine did not reboot")
return err
if len(inst.cfg.USBDevNums) > 0 {
log.Logf(2, "isolated: trying to reboot by USB authorization")
usbAuth := fmt.Sprintf("%s%s%s", "/sys/bus/usb/devices/", inst.cfg.USBDevNums[inst.index], "/authorized")
if err := ioutil.WriteFile(usbAuth, []byte("0"), 0); err != nil {
log.Logf(2, "isolated: failed to turn off the device")
return err
}
if err := ioutil.WriteFile(usbAuth, []byte("1"), 0); err != nil {
log.Logf(2, "isolated: failed to turn on the device")
return err
}
} else {
log.Logf(2, "isolated: ssh succeeded, trying to reboot by ssh")
inst.ssh("reboot") // reboot will return an error, ignore it
}
log.Logf(2, "isolated: rebooted wait for comeback")
if err := inst.waitForSSH(30 * time.Minute); err != nil {
log.Logf(2, "isolated: machine did not comeback")
return err
}
log.Logf(2, "isolated: reboot succeeded")
} else {
log.Logf(2, "isolated: ssh succeeded")
}
if err := inst.waitForReboot(5 * 60); err != nil {
log.Logf(2, "isolated: machine did not reboot")
return err
}
log.Logf(2, "isolated: rebooted wait for comeback")
if err := inst.waitForSSH(30 * time.Minute); err != nil {
log.Logf(0, "isolated: machine did not comeback")
return err
}
log.Logf(2, "isolated: reboot succeeded")
} else {
log.Logf(2, "isolated: ssh failed")
return fmt.Errorf("SSH failed")
}
return nil
}