Best Syzkaller code snippet using prog.generate
init_alg.go
Source:init_alg.go
...4import (5 "math/rand"6 "github.com/google/syzkaller/prog"7)8func (arch *arch) generateSockaddrAlg(g *prog.Gen, typ0 prog.Type, dir prog.Dir, old prog.Arg) (9 arg prog.Arg, calls []*prog.Call) {10 typ := typ0.(*prog.StructType)11 family := g.GenerateArg(typ.Fields[0].Type, dir, &calls)12 // There is very little point in generating feat/mask,13 // because that can only fail otherwise correct bind.14 feat := prog.MakeConstArg(typ.Fields[2].Type, dir, 0)15 mask := prog.MakeConstArg(typ.Fields[3].Type, dir, 0)16 if g.NOutOf(1, 1000) {17 feat = g.GenerateArg(typ.Fields[2].Type, dir, &calls).(*prog.ConstArg)18 mask = g.GenerateArg(typ.Fields[3].Type, dir, &calls).(*prog.ConstArg)19 }20 algType, algName := generateAlgName(g.Rand())21 // Extend/truncate type/name to their fixed sizes.22 algTypeData := fixedSizeData(algType, typ.Fields[1].Type.Size())23 algNameData := fixedSizeData(algName, typ.Fields[4].Type.Size())24 arg = prog.MakeGroupArg(typ, dir, []prog.Arg{25 family,26 prog.MakeDataArg(typ.Fields[1].Type, dir, algTypeData),27 feat,28 mask,29 prog.MakeDataArg(typ.Fields[4].Type, dir, algNameData),30 })31 return32}33func (arch *arch) generateAlgName(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (34 arg prog.Arg, calls []*prog.Call) {35 return generateAlgNameStruct(g, typ, dir, allTypes[g.Rand().Intn(len(allTypes))].typ)36}37func (arch *arch) generateAlgAeadName(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (38 arg prog.Arg, calls []*prog.Call) {39 return generateAlgNameStruct(g, typ, dir, ALG_AEAD)40}41func (arch *arch) generateAlgHashName(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (42 arg prog.Arg, calls []*prog.Call) {43 return generateAlgNameStruct(g, typ, dir, ALG_HASH)44}45func (arch *arch) generateAlgSkcipherhName(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (46 arg prog.Arg, calls []*prog.Call) {47 return generateAlgNameStruct(g, typ, dir, ALG_SKCIPHER)48}49func generateAlgNameStruct(g *prog.Gen, typ0 prog.Type, dir prog.Dir, algTyp int) (50 arg prog.Arg, calls []*prog.Call) {51 typ := typ0.(*prog.StructType)52 algName := generateAlg(g.Rand(), algTyp)53 algNameData := fixedSizeData(algName, typ.Fields[0].Size())54 arg = prog.MakeGroupArg(typ, dir, []prog.Arg{55 prog.MakeDataArg(typ.Fields[0].Type, dir, algNameData),56 })57 return58}59func generateAlgName(rnd *rand.Rand) (string, string) {60 typ := allTypes[rnd.Intn(len(allTypes))]61 name := generateAlg(rnd, typ.typ)62 return typ.name, name63}64func generateAlg(rnd *rand.Rand, typ int) string {65 algs := allAlgs[typ]66 alg := algs[rnd.Intn(len(algs))]67 return generateAlgImpl(rnd, alg)68}69func generateAlgImpl(rnd *rand.Rand, alg algDesc) string {70 args := ""71 if len(alg.args) != 0 {72 args += "("73 for i, a := range alg.args {74 if i != 0 {75 args += ","76 }77 args += generateAlg(rnd, a)78 }79 args += ")"80 }81 return alg.name + args82}83func fixedSizeData(str string, sz uint64) []byte {84 return append([]byte(str), make([]byte, sz)...)[:sz]85}86type algType struct {87 name string88 typ int89}90type algDesc struct {91 name string...
init.go
Source:init.go
...46 target.MakeDataMmap = targets.MakePosixMmap(target, true, true)47 target.Neutralize = arch.neutralize48 target.SpecialTypes = map[string]func(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (49 prog.Arg, []*prog.Call){50 "timespec": arch.generateTimespec,51 "timeval": arch.generateTimespec,52 "sockaddr_alg": arch.generateSockaddrAlg,53 "alg_name": arch.generateAlgName,54 "alg_aead_name": arch.generateAlgAeadName,55 "alg_hash_name": arch.generateAlgHashName,56 "alg_skcipher_name": arch.generateAlgSkcipherhName,57 "ipt_replace": arch.generateIptables,58 "ip6t_replace": arch.generateIptables,59 "arpt_replace": arch.generateArptables,60 "ebt_replace": arch.generateEbtables,61 "usb_device_descriptor": arch.generateUsbDeviceDescriptor,62 "usb_device_descriptor_hid": arch.generateUsbHidDeviceDescriptor,63 }64 target.AuxResources = map[string]bool{65 "uid": true,66 "pid": true,67 "gid": true,68 "timespec": true,69 "timeval": true,70 "time_sec": true,71 "time_usec": true,72 "time_nsec": true,73 }74 switch target.Arch {75 case "amd64":76 target.SpecialPointers = []uint64{77 0xffffffff81000000, // kernel text78 }79 case "386", "arm64", "arm", "ppc64le", "mips64le", "s390x", "riscv64":80 default:81 panic("unknown arch")82 }83 if target.Arch == runtime.GOARCH {84 KCOV_INIT_TRACE = uintptr(target.GetConst("KCOV_INIT_TRACE"))85 KCOV_ENABLE = uintptr(target.GetConst("KCOV_ENABLE"))86 KCOV_REMOTE_ENABLE = uintptr(target.GetConst("KCOV_REMOTE_ENABLE"))87 KCOV_DISABLE = uintptr(target.GetConst("KCOV_DISABLE"))88 KCOV_TRACE_PC = uintptr(target.GetConst("KCOV_TRACE_PC"))89 KCOV_TRACE_CMP = uintptr(target.GetConst("KCOV_TRACE_CMP"))90 }91}92var (93 // This should not be here, but for now we expose this for syz-fuzzer.94 KCOV_INIT_TRACE uintptr95 KCOV_ENABLE uintptr96 KCOV_REMOTE_ENABLE uintptr97 KCOV_DISABLE uintptr98 KCOV_TRACE_PC uintptr99 KCOV_TRACE_CMP uintptr100)101type arch struct {102 unix *targets.UnixNeutralizer103 clockGettimeSyscall *prog.Syscall104 MREMAP_MAYMOVE uint64105 MREMAP_FIXED uint64106 SYSLOG_ACTION_CONSOLE_OFF uint64107 SYSLOG_ACTION_CONSOLE_ON uint64108 SYSLOG_ACTION_CONSOLE_LEVEL uint64109 SYSLOG_ACTION_CLEAR uint64110 SYSLOG_ACTION_SIZE_UNREAD uint64111 FIFREEZE uint64112 FITHAW uint64113 SNAPSHOT_FREEZE uint64114 SNAPSHOT_POWER_OFF uint64115 EXT4_IOC_SHUTDOWN uint64116 EXT4_IOC_RESIZE_FS uint64117 EXT4_IOC_MIGRATE uint64118 FAN_OPEN_PERM uint64119 FAN_ACCESS_PERM uint64120 FAN_OPEN_EXEC_PERM uint64121 PTRACE_TRACEME uint64122 CLOCK_REALTIME uint64123 ARCH_SET_FS uint64124 ARCH_SET_GS uint64125 AF_NFC uint64126 AF_LLC uint64127 AF_BLUETOOTH uint64128 AF_X25 uint64129 AF_AX25 uint64130 AF_NETROM uint64131 AF_ROSE uint64132 USB_MAJOR uint64133 TIOCSSERIAL uint64134 TIOCGSERIAL uint64135}136func (arch *arch) neutralize(c *prog.Call) {137 arch.unix.Neutralize(c)138 switch c.Meta.CallName {139 case "mremap":140 // Add MREMAP_FIXED flag, otherwise it produces non-deterministic results.141 flags := c.Args[3].(*prog.ConstArg)142 if flags.Val&arch.MREMAP_MAYMOVE != 0 {143 flags.Val |= arch.MREMAP_FIXED144 }145 case "syslog":146 cmd := c.Args[0].(*prog.ConstArg)147 cmd.Val = uint64(uint32(cmd.Val))148 // These disable console output, but we need it.149 if cmd.Val == arch.SYSLOG_ACTION_CONSOLE_OFF ||150 cmd.Val == arch.SYSLOG_ACTION_CONSOLE_ON ||151 cmd.Val == arch.SYSLOG_ACTION_CONSOLE_LEVEL ||152 cmd.Val == arch.SYSLOG_ACTION_CLEAR {153 cmd.Val = arch.SYSLOG_ACTION_SIZE_UNREAD154 }155 case "ioctl":156 arch.neutralizeIoctl(c)157 case "fanotify_mark":158 // FAN_*_PERM require the program to reply to open requests.159 // If that does not happen, the program will hang in an unkillable state forever.160 // See the following bug for details:161 // https://groups.google.com/d/msg/syzkaller-bugs/pD-vbqJu6U0/kGH30p3lBgAJ162 mask := c.Args[2].(*prog.ConstArg)163 mask.Val &^= arch.FAN_OPEN_PERM | arch.FAN_ACCESS_PERM | arch.FAN_OPEN_EXEC_PERM164 case "ptrace":165 req := c.Args[0].(*prog.ConstArg)166 // PTRACE_TRACEME leads to unkillable processes, see:167 // https://groups.google.com/forum/#!topic/syzkaller/uGzwvhlCXAw168 if uint64(uint32(req.Val)) == arch.PTRACE_TRACEME {169 req.Val = ^uint64(0)170 }171 case "arch_prctl":172 // fs holds address of tls, if a program messes it at least signal173 // handling will break. This also allows a program to do writes174 // at arbitrary addresses, which usually leads to machine outbreak.175 cmd := c.Args[0].(*prog.ConstArg)176 if uint64(uint32(cmd.Val)) == arch.ARCH_SET_FS {177 cmd.Val = arch.ARCH_SET_GS178 }179 case "init_module":180 // Kernel tries to vmalloc whatever we pass as size and it's not accounted against memcg.181 // As the result it can lead to massive OOM kills of everything running on the machine.182 // Strictly saying, the same applies to finit_module with a sparse file too,183 // but there is no simple way to handle that.184 sz := c.Args[1].(*prog.ConstArg)185 sz.Val %= 1 << 20186 case "syz_init_net_socket":187 // Don't let it mess with arbitrary sockets in init namespace.188 family := c.Args[0].(*prog.ConstArg)189 switch uint64(uint32(family.Val)) {190 case arch.AF_NFC, arch.AF_LLC, arch.AF_BLUETOOTH,191 arch.AF_X25, arch.AF_AX25, arch.AF_NETROM, arch.AF_ROSE:192 default:193 family.Val = ^uint64(0)194 }195 case "syz_open_dev":196 enforceIntArg(c.Args[0])197 enforceIntArg(c.Args[1])198 enforceIntArg(c.Args[2])199 }200 switch c.Meta.Name {201 case "setsockopt$EBT_SO_SET_ENTRIES":202 arch.neutralizeEbtables(c)203 }204}205func enforceIntArg(a prog.Arg) {206 arg, ok := a.(*prog.ConstArg)207 if !ok {208 return209 }210 switch typ := arg.Type().(type) {211 case *prog.ConstType:212 arg.Val = typ.Val213 case *prog.IntType:214 if typ.Kind == prog.IntRange && (arg.Val < typ.RangeBegin || arg.Val > typ.RangeEnd) {215 arg.Val = typ.RangeBegin216 }217 }218}219func (arch *arch) neutralizeIoctl(c *prog.Call) {220 cmd := c.Args[1].(*prog.ConstArg)221 switch uint64(uint32(cmd.Val)) {222 case arch.FIFREEZE:223 // Freeze kills machine. Though, it is an interesting functions,224 // so we need to test it somehow.225 // TODO: not required if executor drops privileges.226 // Fortunately, the value does not conflict with any other ioctl commands for now.227 cmd.Val = arch.FITHAW228 case arch.SNAPSHOT_FREEZE:229 // SNAPSHOT_FREEZE freezes all processes and leaves the machine dead.230 cmd.Val = arch.FITHAW231 case arch.SNAPSHOT_POWER_OFF:232 // SNAPSHOT_POWER_OFF shuts down the machine.233 cmd.Val = arch.FITHAW234 case arch.EXT4_IOC_SHUTDOWN:235 // EXT4_IOC_SHUTDOWN on root fs effectively brings the machine down in weird ways.236 // Fortunately, the value does not conflict with any other ioctl commands for now.237 cmd.Val = arch.EXT4_IOC_MIGRATE238 case arch.EXT4_IOC_RESIZE_FS:239 // EXT4_IOC_RESIZE_FS on root fs can shrink it to 0 (or whatever is the minimum size)240 // and then creation of new temp dirs for tests will fail.241 // TODO: not necessary for sandbox=namespace as it tests in a tmpfs242 // and/or if we mount tmpfs for sandbox=none (#971).243 cmd.Val = arch.EXT4_IOC_MIGRATE244 case arch.TIOCSSERIAL:245 // TIOCSSERIAL can do nasty things under root, like causing writes to random memory246 // pretty much like /dev/mem, but this is also working as intended.247 // For details see:248 // https://groups.google.com/g/syzkaller-bugs/c/1rVENJf9P4U/m/QtGpapRxAgAJ249 // https://syzkaller.appspot.com/bug?extid=f4f1e871965064ae689e250 // TODO: TIOCSSERIAL does some other things that are not dangerous251 // and would be nice to test, if/when we can neutralize based on sandbox value252 // we could prohibit it only under sandbox=none.253 cmd.Val = arch.TIOCGSERIAL254 }255}256func (arch *arch) generateTimespec(g *prog.Gen, typ0 prog.Type, dir prog.Dir, old prog.Arg) (257 arg prog.Arg, calls []*prog.Call) {258 typ := typ0.(*prog.StructType)259 // We need to generate timespec/timeval that are either260 // (1) definitely in the past, or261 // (2) definitely in unreachable fututre, or262 // (3) few ms ahead of now.263 // Note: timespec/timeval can be absolute or relative to now.264 // Note: executor has blocking syscall timeout of 45 ms,265 // so we generate both 10ms and 60ms.266 const (267 timeout1 = uint64(10)268 timeout2 = uint64(60)269 )270 usec := typ.Name() == "timeval"271 switch {272 case g.NOutOf(1, 4):273 // Now for relative, past for absolute.274 arg = prog.MakeGroupArg(typ, dir, []prog.Arg{275 prog.MakeResultArg(typ.Fields[0].Type, dir, nil, 0),276 prog.MakeResultArg(typ.Fields[1].Type, dir, nil, 0),277 })278 case g.NOutOf(1, 3):279 // Few ms ahead for relative, past for absolute....
init_iptables.go
Source:init_iptables.go
...4import (5 "strings"6 "github.com/google/syzkaller/prog"7)8func (arch *arch) generateIptables(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (9 arg prog.Arg, calls []*prog.Call) {10 return arch.generateNetfilterTable(g, typ, dir, old, true, 5)11}12func (arch *arch) generateArptables(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (13 arg prog.Arg, calls []*prog.Call) {14 return arch.generateNetfilterTable(g, typ, dir, old, false, 3)15}16func (arch *arch) generateNetfilterTable(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg,17 hasUnion bool, hookCount int) (arg prog.Arg, calls []*prog.Call) {18 const (19 hookStart = 420 nonHookFields = 721 unused = uint64(^uint32(0))22 )23 if old == nil {24 arg = g.GenerateSpecialArg(typ, dir, &calls)25 } else {26 // TODO(dvyukov): try to restore original hook order after mutation27 // instead of assigning brand new offsets.28 arg = old29 calls = g.MutateArg(arg)30 }31 var tableArg *prog.GroupArg32 if hasUnion {33 tableArg = arg.(*prog.UnionArg).Option.(*prog.GroupArg)34 } else {35 tableArg = arg.(*prog.GroupArg)36 }37 numFileds := nonHookFields + 2*hookCount38 if len(tableArg.Inner) != numFileds {39 panic("wrong number of fields in netfilter table")40 }41 entriesArg := tableArg.Inner[numFileds-1].(*prog.GroupArg)42 if len(entriesArg.Inner) != 2 {43 panic("netfilter entries is expected to have 2 fields")44 }45 entriesArray := entriesArg.Inner[0].(*prog.GroupArg)46 // Collect offsets of entries.47 offsets := make([]uint64, len(entriesArray.Inner))48 var pos uint6449 for i, entryArg := range entriesArray.Inner {50 offsets[i] = pos51 pos += entryArg.Size()52 }53 if pos != entriesArray.Size() {54 panic("netfilter offsets are broken")55 }56 genOffset := func() uint64 {57 if g.Rand().Intn(100) == 0 {58 // Assign the underflow entry once in a while.59 // We have it in underflow hooks, so no point in using it frequently.60 return pos61 }62 return offsets[g.Rand().Intn(len(offsets))]63 }64 // Assign offsets to used hooks.65 for hook := hookStart; hook < hookStart+hookCount; hook++ {66 hookArg := tableArg.Inner[hook].(*prog.ConstArg)67 if hookArg.Type().(*prog.ConstType).Val == unused {68 continue // unused hook69 }70 hookArg.Val = genOffset()71 }72 // Assign offsets to used underflow entries.73 for hook := hookStart + hookCount; hook < hookStart+2*hookCount; hook++ {74 hookArg := tableArg.Inner[hook].(*prog.ConstArg)75 if hookArg.Type().(*prog.ConstType).Val == unused {76 continue // unused hook77 }78 hookArg.Val = pos79 }80 // Now update standard target jump offsets.81 prog.ForeachSubArg(arg, func(arg prog.Arg, _ *prog.ArgCtx) {82 if !strings.HasPrefix(arg.Type().Name(), `xt_target_t["", `) {83 return84 }85 targetArg := arg.(*prog.GroupArg)86 valArg := targetArg.Inner[3].(*prog.ConstArg)87 flagsType, ok := valArg.Type().(*prog.FlagsType)88 if !ok {89 return90 }91 if int64(valArg.Val) < 0 {92 for _, val := range flagsType.Vals {93 if val == valArg.Val {94 return // verdict95 }96 }97 }98 valArg.Val = genOffset()99 })100 return101}102func (arch *arch) generateEbtables(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (103 arg prog.Arg, calls []*prog.Call) {104 if old == nil {105 arg = g.GenerateSpecialArg(typ, dir, &calls)106 } else {107 // TODO(dvyukov): try to restore original hook order after mutation108 // instead of assigning brand new offsets.109 arg = old110 calls = g.MutateArg(arg)111 }112 if g.Target().ArgContainsAny(arg) {113 return114 }115 hooksField, entriesField := 4, 7116 if g.Target().PtrSize == 8 {...
generate
Using AI Code Generation
1import "fmt"2func main() {3 p := prog{1, "golang"}4 p.generate()5}6import "fmt"7func main() {8 p := prog{1, "golang"}9 p.generate()10}11import "fmt"12func main() {13 p := prog{1, "golang"}14 p.generate()15}16import "fmt"17func main() {18 p := prog{1, "golang"}19 p.generate()20}21import "fmt"22func main() {23 p := prog{1, "golang"}24 p.generate()25}26import "fmt"27func main() {28 p := prog{1, "golang"}29 p.generate()30}31import "fmt"32func main() {33 p := prog{1, "golang"}34 p.generate()35}36import "fmt"37func main() {38 p := prog{1, "golang"}39 p.generate()40}41import "fmt"42func main() {43 p := prog{1, "golang"}44 p.generate()45}46import "fmt"47func main() {48 p := prog{1, "golang"}49 p.generate()50}51import "fmt"52func main() {53 p := prog{1, "golang"}54 p.generate()55}56import "fmt"57func main() {
generate
Using AI Code Generation
1import "fmt"2func main() {3 p := prog{}4 p.generate()5}6import "fmt"7type prog struct{}8func (p prog) generate() {9 fmt.Println("generating code")10}
generate
Using AI Code Generation
1import (2func main() {3 p := new(prog)4 p.generate()5}6import (7func main() {8 p := new(prog)9 p.generate()10}11import (12func main() {13 p := new(prog)14 p.generate()15}16import (17func main() {18 p := new(prog)19 p.generate()20}21import (22func main() {23 p := new(prog)24 p.generate()25}26import (27func main() {28 p := new(prog)29 p.generate()30}31import (32func main() {33 p := new(prog)34 p.generate()35}36import (37func main() {38 p := new(prog)39 p.generate()40}41import (42func main() {43 p := new(prog)44 p.generate()45}46import (47func main() {
generate
Using AI Code Generation
1import (2func main() {3 p.generate()4}5import (6type prog struct {7}8func (p prog) generate() {9 fmt.Println("Generate method of prog class")10}11import (12type prog struct {13}14func (p prog) generate() {15 fmt.Println("Generate method of prog class")16}17import (18type prog struct {19}20func (p prog) generate() {21 fmt.Println("Generate method of prog class")22}23import (24type prog struct {25}26func (p prog) generate() {27 fmt.Println("Generate method of prog class")28}29import (30type prog struct {31}32func (p prog) generate() {33 fmt.Println("Generate method of prog class")34}35import (36type prog struct {37}38func (p prog) generate() {39 fmt.Println("Generate method of prog class")40}41import (42type prog struct {43}44func (p prog) generate() {45 fmt.Println("Generate method of prog class")46}47import (48type prog struct {49}50func (p prog) generate() {51 fmt.Println("Generate method of prog class")52}53import (54type prog struct {55}56func (p prog) generate() {57 fmt.Println("Generate method of prog class")58}
generate
Using AI Code Generation
1import "fmt"2func main() {3 prog := new(Prog)4 prog.Generate()5 fmt.Println(prog)6}7import "fmt"8func main() {9 prog := new(Prog)10 prog.Generate()11 fmt.Println(prog)12}13import "fmt"14func main() {15 prog := new(Prog)16 prog.Generate()17 fmt.Println(prog)18}19import "fmt"20func main() {21 prog := new(Prog)22 prog.Generate()23 fmt.Println(prog)24}25import "fmt"26func main() {27 prog := new(Prog)28 prog.Generate()29 fmt.Println(prog)30}31import "fmt"32func main() {33 prog := new(Prog)34 prog.Generate()35 fmt.Println(prog)36}37import "fmt"38func main() {39 prog := new(Prog)40 prog.Generate()41 fmt.Println(prog)42}43import "fmt"44func main() {45 prog := new(Prog)46 prog.Generate()
generate
Using AI Code Generation
1import (2func main() {3 }4 p.generate()5}6import (7func main() {8 }9 p.generate()10}11import (12func main() {13 }14 p.generate()15}16import (17func main() {18 }19 p.generate()20}21import (22func main() {23 }24 p.generate()25}26import (27func main() {28 }29 p.generate()30}31import (32func main() {33 }34 p.generate()35}36import (37func main() {38 }39 p.generate()40}41import (
generate
Using AI Code Generation
1import "fmt"2func main() {3 p := prog{name: "Naveen", age: 50}4 p.generate()5}6import "fmt"7type prog struct {8}9func (p prog) generate() {10 fmt.Println(p.name, p.age)11}12import "fmt"13func main() {14 p := prog{name: "Naveen", age: 50}15 p.generate()16 p.generate()17}18import "fmt"19type prog struct {20}21func (p prog) generate() {22 fmt.Println(p.name, p.age)23}24import "fmt"25func main() {26 p := prog{name: "Naveen", age: 50}27 p.generate()28 p.generate()29 p.update()
generate
Using AI Code Generation
1import (2func main() {3 p.generate()4 fmt.Println(p)5}6{0 0}
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!