Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 7bcc41c

Browse files
nharper285chkeita
authored andcommitted
Disable repro and debug VM CLI commands. (#3494)
* Disable and VM CLI commands. * Formatting. * More formatting. * More formatting. * Removing Repro check.
1 parent ecb2d32 commit 7bcc41c

File tree

3 files changed

+9
-519
lines changed

3 files changed

+9
-519
lines changed

src/cli/onefuzz/api.py

Lines changed: 1 addition & 314 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import pkgutil
1010
import re
1111
import subprocess # nosec
12-
import time
1312
import uuid
1413
from enum import Enum
1514
from shutil import which
@@ -35,8 +34,7 @@
3534

3635
from .__version__ import __version__
3736
from .azcopy import azcopy_sync
38-
from .backend import Backend, BackendConfig, ContainerWrapper, wait
39-
from .ssh import build_ssh_command, ssh_connect, temp_file
37+
from .backend import Backend, BackendConfig, ContainerWrapper
4038

4139
UUID_EXPANSION = TypeVar("UUID_EXPANSION", UUID, str)
4240

@@ -530,316 +528,6 @@ def _download_tasks(
530528
azcopy_sync(to_download[name], outdir)
531529

532530

533-
class Repro(Endpoint):
534-
"""Interact with Reproduction VMs"""
535-
536-
endpoint = "repro_vms"
537-
538-
def get(self, vm_id: UUID_EXPANSION) -> models.Repro:
539-
"""get information about a Reproduction VM"""
540-
vm_id_expanded = self._disambiguate_uuid(
541-
"vm_id", vm_id, lambda: [str(x.vm_id) for x in self.list()]
542-
)
543-
544-
self.logger.debug("get repro vm: %s", vm_id_expanded)
545-
return self._req_model(
546-
"GET", models.Repro, data=requests.ReproGet(vm_id=vm_id_expanded)
547-
)
548-
549-
def get_files(
550-
self,
551-
report_container: primitives.Container,
552-
report_name: str,
553-
include_setup: bool = False,
554-
output_dir: primitives.Directory = primitives.Directory("."),
555-
) -> None:
556-
"""downloads the files necessary to locally repro the crash from a given report"""
557-
report_bytes = self.onefuzz.containers.files.get(report_container, report_name)
558-
report = json.loads(report_bytes)
559-
560-
crash_info = {
561-
"input_blob_container": primitives.Container(""),
562-
"input_blob_name": "",
563-
"job_id": "",
564-
}
565-
if "input_blob" in report:
566-
crash_info["input_blob_container"] = report["input_blob"]["container"]
567-
crash_info["input_blob_name"] = report["input_blob"]["name"]
568-
crash_info["job_id"] = report["job_id"]
569-
elif "crash_test_result" in report and "original_crash_test_result" in report:
570-
if report["original_crash_test_result"]["crash_report"] is None:
571-
self.logger.error(
572-
"No crash report found in the original crash test result, repro files cannot be retrieved"
573-
)
574-
return
575-
elif report["crash_test_result"]["crash_report"] is None:
576-
self.logger.info(
577-
"No crash report found in the new crash test result, falling back on the original crash test result for job_id"
578-
"Note: if using --include_setup, the downloaded fuzzer binaries may be out-of-date"
579-
)
580-
581-
original_report = report["original_crash_test_result"]["crash_report"]
582-
new_report = (
583-
report["crash_test_result"]["crash_report"] or original_report
584-
) # fallback on original_report
585-
586-
crash_info["input_blob_container"] = original_report["input_blob"][
587-
"container"
588-
]
589-
crash_info["input_blob_name"] = original_report["input_blob"]["name"]
590-
crash_info["job_id"] = new_report["job_id"]
591-
else:
592-
self.logger.error(
593-
"Encountered an unhandled report format, repro files cannot be retrieved"
594-
)
595-
return
596-
597-
self.logger.info(
598-
"downloading files necessary to locally repro crash %s",
599-
crash_info["input_blob_name"],
600-
)
601-
self.onefuzz.containers.files.download(
602-
primitives.Container(crash_info["input_blob_container"]),
603-
crash_info["input_blob_name"],
604-
os.path.join(output_dir, crash_info["input_blob_name"]),
605-
)
606-
607-
if include_setup:
608-
setup_container = list(
609-
self.onefuzz.jobs.containers.list(
610-
crash_info["job_id"], enums.ContainerType.setup
611-
)
612-
)[0]
613-
614-
self.onefuzz.containers.files.download_dir(
615-
primitives.Container(setup_container), output_dir
616-
)
617-
618-
def create(
619-
self, container: primitives.Container, path: str, duration: int = 24
620-
) -> models.Repro:
621-
"""Create a Reproduction VM from a Crash Report"""
622-
self.logger.info(
623-
"creating repro vm: %s %s (%d hours)", container, path, duration
624-
)
625-
return self._req_model(
626-
"POST",
627-
models.Repro,
628-
data=models.ReproConfig(container=container, path=path, duration=duration),
629-
)
630-
631-
def delete(self, vm_id: UUID_EXPANSION) -> models.Repro:
632-
"""Delete a Reproduction VM"""
633-
vm_id_expanded = self._disambiguate_uuid(
634-
"vm_id", vm_id, lambda: [str(x.vm_id) for x in self.list()]
635-
)
636-
637-
self.logger.debug("deleting repro vm: %s", vm_id_expanded)
638-
return self._req_model(
639-
"DELETE", models.Repro, data=requests.ReproGet(vm_id=vm_id_expanded)
640-
)
641-
642-
def list(self) -> List[models.Repro]:
643-
"""List all VMs"""
644-
self.logger.debug("listing repro vms")
645-
return self._req_model_list("GET", models.Repro, data=requests.ReproGet())
646-
647-
def _dbg_linux(
648-
self, repro: models.Repro, debug_command: Optional[str]
649-
) -> Optional[str]:
650-
"""Launch gdb with GDB script that includes 'target remote | ssh ...'"""
651-
652-
if (
653-
repro.auth is None
654-
or repro.ip is None
655-
or repro.state != enums.VmState.running
656-
):
657-
raise Exception("vm setup failed: %s" % repro.state)
658-
659-
with build_ssh_command(
660-
repro.ip, repro.auth.private_key, command="-T"
661-
) as ssh_cmd:
662-
gdb_script = [
663-
"target remote | %s sudo /onefuzz/bin/repro-stdout.sh"
664-
% " ".join(ssh_cmd)
665-
]
666-
667-
if debug_command:
668-
gdb_script += [debug_command, "quit"]
669-
670-
with temp_file("gdb.script", "\n".join(gdb_script)) as gdb_script_path:
671-
dbg = ["gdb", "--silent", "--command", gdb_script_path]
672-
673-
if debug_command:
674-
dbg += ["--batch"]
675-
676-
try:
677-
# security note: dbg is built from content coming from
678-
# the server, which is trusted in this context.
679-
return subprocess.run( # nosec
680-
dbg, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
681-
).stdout.decode(errors="ignore")
682-
except subprocess.CalledProcessError as err:
683-
self.logger.error(
684-
"debug failed: %s", err.output.decode(errors="ignore")
685-
)
686-
raise err
687-
else:
688-
# security note: dbg is built from content coming from the
689-
# server, which is trusted in this context.
690-
subprocess.call(dbg) # nosec
691-
return None
692-
693-
def _dbg_windows(
694-
self,
695-
repro: models.Repro,
696-
debug_command: Optional[str],
697-
retry_limit: Optional[int],
698-
) -> Optional[str]:
699-
"""Setup an SSH tunnel, then connect via CDB over SSH tunnel"""
700-
701-
if (
702-
repro.auth is None
703-
or repro.ip is None
704-
or repro.state != enums.VmState.running
705-
):
706-
raise Exception("vm setup failed: %s" % repro.state)
707-
708-
retry_count = 0
709-
bind_all = which("wslpath") is not None and repro.os == enums.OS.windows
710-
proxy = "*:" + REPRO_SSH_FORWARD if bind_all else REPRO_SSH_FORWARD
711-
while retry_limit is None or retry_count <= retry_limit:
712-
if retry_limit:
713-
retry_count = retry_count + 1
714-
with ssh_connect(repro.ip, repro.auth.private_key, proxy=proxy):
715-
dbg = ["cdb.exe", "-remote", "tcp:port=1337,server=localhost"]
716-
if debug_command:
717-
dbg_script = [debug_command, "qq"]
718-
with temp_file(
719-
"db.script", "\r\n".join(dbg_script)
720-
) as dbg_script_path:
721-
dbg += ["-cf", _wsl_path(dbg_script_path)]
722-
723-
logging.debug("launching: %s", dbg)
724-
try:
725-
# security note: dbg is built from content coming from the server,
726-
# which is trusted in this context.
727-
return subprocess.run( # nosec
728-
dbg, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
729-
).stdout.decode(errors="ignore")
730-
except subprocess.CalledProcessError as err:
731-
if err.returncode == 0x8007274D:
732-
self.logger.info(
733-
"failed to connect to debug-server trying again in 10 seconds..."
734-
)
735-
time.sleep(10.0)
736-
else:
737-
self.logger.error(
738-
"debug failed: %s",
739-
err.output.decode(errors="ignore"),
740-
)
741-
raise err
742-
else:
743-
logging.debug("launching: %s", dbg)
744-
# security note: dbg is built from content coming from the
745-
# server, which is trusted in this context.
746-
try:
747-
subprocess.check_call(dbg) # nosec
748-
return None
749-
except subprocess.CalledProcessError as err:
750-
if err.returncode == 0x8007274D:
751-
self.logger.info(
752-
"failed to connect to debug-server trying again in 10 seconds..."
753-
)
754-
time.sleep(10.0)
755-
else:
756-
return None
757-
758-
if retry_limit is not None:
759-
self.logger.info(
760-
f"failed to connect to debug-server after {retry_limit} attempts. Please try again later "
761-
+ f"with onefuzz debug connect {repro.vm_id}"
762-
)
763-
return None
764-
765-
def connect(
766-
self,
767-
vm_id: UUID_EXPANSION,
768-
delete_after_use: bool = False,
769-
debug_command: Optional[str] = None,
770-
retry_limit: Optional[int] = None,
771-
) -> Optional[str]:
772-
"""Connect to an existing Reproduction VM"""
773-
774-
self.logger.info("connecting to reproduction VM: %s", vm_id)
775-
776-
if which("ssh") is None:
777-
raise Exception("unable to find ssh on local machine")
778-
779-
def missing_os() -> Tuple[bool, str, models.Repro]:
780-
repro = self.get(vm_id)
781-
return (
782-
repro.os is not None,
783-
"waiting for os determination",
784-
repro,
785-
)
786-
787-
repro = wait(missing_os)
788-
789-
if repro.os == enums.OS.windows:
790-
if which("cdb.exe") is None:
791-
raise Exception("unable to find cdb.exe on local machine")
792-
if repro.os == enums.OS.linux:
793-
if which("gdb") is None:
794-
raise Exception("unable to find gdb on local machine")
795-
796-
def func() -> Tuple[bool, str, models.Repro]:
797-
repro = self.get(vm_id)
798-
state = repro.state
799-
return (
800-
repro.auth is not None
801-
and repro.ip is not None
802-
and state not in [enums.VmState.init, enums.VmState.extensions_launch],
803-
"launching reproducing vm. current state: %s" % state,
804-
repro,
805-
)
806-
807-
repro = wait(func)
808-
# give time for debug server to initialize
809-
time.sleep(30.0)
810-
result: Optional[str] = None
811-
if repro.os == enums.OS.windows:
812-
result = self._dbg_windows(repro, debug_command, retry_limit)
813-
elif repro.os == enums.OS.linux:
814-
result = self._dbg_linux(repro, debug_command)
815-
else:
816-
raise NotImplementedError
817-
818-
if delete_after_use:
819-
self.logger.debug("deleting vm %s", repro.vm_id)
820-
self.delete(repro.vm_id)
821-
822-
return result
823-
824-
def create_and_connect(
825-
self,
826-
container: primitives.Container,
827-
path: str,
828-
duration: int = 24,
829-
delete_after_use: bool = False,
830-
debug_command: Optional[str] = None,
831-
retry_limit: Optional[int] = None,
832-
) -> Optional[str]:
833-
"""Create and connect to a Reproduction VM"""
834-
repro = self.create(container, path, duration=duration)
835-
return self.connect(
836-
repro.vm_id,
837-
delete_after_use=delete_after_use,
838-
debug_command=debug_command,
839-
retry_limit=retry_limit,
840-
)
841-
842-
843531
class Notifications(Endpoint):
844532
"""Interact with models.Notifications"""
845533

@@ -1900,7 +1588,6 @@ def __init__(
19001588
client_secret=client_secret,
19011589
)
19021590
self.containers = Containers(self)
1903-
self.repro = Repro(self)
19041591
self.notifications = Notifications(self)
19051592
self.tasks = Tasks(self)
19061593
self.jobs = Jobs(self)

0 commit comments

Comments
 (0)