# Copyright 2018 syzkaller project authors. All rights reserved.
# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

include <uapi/linux/a.out.h>
include <uapi/linux/elf.h>
include <linux/fcntl.h>

resource fd_binfmt[fd]
resource fd_binfmt_register[fd]
resource fd_binfmt_format[fd]
resource ptr_binfmt_file[intptr]

syz_create_resource$binfmt(file ptr[in, filename]) ptr_binfmt_file

execve(file ptr[in, filename], argv ptr[in, argv_array], envp ptr[in, argv_array])
execveat(dirfd fd_dir, file ptr[in, filename], argv ptr[in, argv_array], envp ptr[in, argv_array], flags flags[at_flags])
execveat$binfmt(dirfd fd_dir, file ptr_binfmt_file, argv ptr[in, argv_array], envp ptr[in, argv_array], flags flags[at_flags])

openat$binfmt(fd const[AT_FDCWD], file ptr_binfmt_file, flags const[BINFMT_OPEN_FLAGS], mode const[0x1ff]) fd_binfmt

write$binfmt_script(fd fd_binfmt, data ptr[in, binfmt_script], len bytesize[data])
write$binfmt_misc(fd fd_binfmt, data ptr[in, array[int8]], len bytesize[data])
write$binfmt_aout(fd fd_binfmt, data ptr[in, binfmt_aout], len bytesize[data])
write$binfmt_elf32(fd fd_binfmt, data ptr[in, binfmt_elf32], len bytesize[data])
write$binfmt_elf64(fd fd_binfmt, data ptr[in, binfmt_elf64], len bytesize[data])

close$binfmt(fd fd_binfmt)

openat$binfmt_register(fd const[AT_FDCWD], file ptr[in, string["/proc/sys/fs/binfmt_misc/register"]], flags const[O_WRONLY], mode const[0]) fd_binfmt_register
write$binfmt_register(fd fd_binfmt_register, data ptr[in, binfmt_register], len bytesize[data])

openat$binfmt_format(fd const[AT_FDCWD], file ptr[in, string[binfmt_format_files]], flags const[O_RDWR], mode const[0]) fd_binfmt_format
write$binfmt_format(fd fd_binfmt_format, data ptr[in, string[binfmt_format_cmd]], len bytesize[data])

define BINFMT_OPEN_FLAGS	O_WRONLY | O_CREAT

argv_array {
	args	array[ptr[in, string]]
	z	const[0, intptr]
} [packed]

# For details of the format see:
# Documentation/admin-guide/binfmt-misc.rst
binfmt_register {
	colon0		const[':', int8]
	name		stringnoz[binfmt_names]
	colon1		const[':', int8]
	type		stringnoz[binfmt_types]
	colon2		const[':', int8]
	offset		fmt[dec, int64]
	colon3		const[':', int8]
	magic		stringnoz
	colon4		const[':', int8]
	mask		stringnoz
	colon5		const[':', int8]
	interpreter	stringnoz[filename]
	colon6		const[':', int8]
	flags		array[flags[binfmt_flags, int8]]
} [packed]

# syz0/1 are pre-registered by executor, but we can delete them and them re-create.
binfmt_names = "syz0", "syz1", "syz2", "syz3"
binfmt_types = "M", "E"
binfmt_flags = 'P', 'O', 'C', 'F'
binfmt_format_cmd = "0", "1", "-1"
binfmt_format_files = "/proc/sys/fs/binfmt_misc/syz0", "/proc/sys/fs/binfmt_misc/syz1", "/proc/sys/fs/binfmt_misc/syz2", "/proc/sys/fs/binfmt_misc/syz3"

binfmt_script {
	hdr	stringnoz["#! "]
	bin	stringnoz[filename]
	args	array[binfmt_script_arg]
	nl	const[0xa, int8]
	data	array[int8]
} [packed]

binfmt_script_arg {
	sp	const[0x20, int8]
	arg	stringnoz
}

binfmt_aout {
	exec	exec
	data	array[int8]
# Just to make the file of a non-trivial size.
	pad	array[array[const[0, int64], 32], 0:10]
} [packed]

exec {
	magic		flags[aouthdr_magics, int16]
	machtype	int8
	flags		int8
	a_text		int32[0:1000]
	a_data		int32[0:1000]
	a_bss		int32
	a_syms		int32[0:1000]
	a_entry		int32
	a_trsize	const[0, int32]
	a_drsize	const[0, int32]
}

aouthdr_magics = OMAGIC, NMAGIC, ZMAGIC, QMAGIC

type binfmt_elf32 binfmt_elf[int32, elf32_phdr, ELF32_PHDR_SIZE]
type binfmt_elf64 binfmt_elf[int64, elf64_phdr, ELF64_PHDR_SIZE]

type binfmt_elf[ADDR, PHDR, PHENTSIZE] {
	hdr	elf_hdr[ADDR, PHENTSIZE]
	phdr	array[PHDR, 1:4]
	data	array[int8]
# Just to make the file of a non-trivial size.
	pad	array[array[const[0, int64], 32], 0:10]
} [packed]

type elf_hdr[ADDR, PHENTSIZE] {
	e_ident0	const[0x7f, int8]
	e_ident1	const[0x45, int8]
	e_ident2	const[0x4c, int8]
	e_ident3	const[0x46, int8]
	e_ident_class	int8
	e_ident_data	int8
	e_ident_ver	int8
	e_ident_osabi	int8
	e_ident_pad	int64
	e_type		flags[elf_types, int16]
	e_machine	flags[elf_machines, int16]
	e_version	int32
	e_entry		ADDR[0:1000]
	e_phoff		bytesize[parent, ADDR]
	e_shoff		ADDR[0:1000]
	e_flags		int32
	e_ehsize	int16
	e_phentsize	const[PHENTSIZE, int16]
	e_phnum		len[binfmt_elf:phdr, int16]
	e_shentsize	int16
	e_shnum		int16
	e_shstrndx	int16
}

elf32_phdr {
	p_type		flags[elf_ptypes, int32]
	p_offset	int32
	p_vaddr		int32
	p_paddr		int32
	p_filesz	int32
	p_memsz		int32
	p_flags		int32
	p_align		int32
} [size[ELF32_PHDR_SIZE]]

elf64_phdr {
	p_type		flags[elf_ptypes, int32]
	p_flags		int32
	p_offset	int64
	p_vaddr		int64
	p_paddr		int64
	p_filesz	int64
	p_memsz		int64
	p_align		int64
} [size[ELF64_PHDR_SIZE]]

elf_types = ET_EXEC, ET_DYN
elf_machines = EM_386, EM_486, EM_X86_64
elf_ptypes = PT_LOAD, PT_DYNAMIC, PT_INTERP, PT_NOTE, PT_SHLIB, PT_PHDR, PT_TLS, PT_LOOS, PT_LOPROC, PT_GNU_STACK

define ELF32_PHDR_SIZE	sizeof(struct elf32_phdr)
define ELF64_PHDR_SIZE	sizeof(struct elf64_phdr)
