github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/emulator/memory.go (about) 1 package emulator 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 7 "github.com/dylandreimerink/gobpfld/ebpf" 8 ) 9 10 // Memory represents memory which can be accessed by the eBPF VM. 11 type Memory interface { 12 Name() string 13 Read(offset int, size ebpf.Size) (RegisterValue, error) 14 ReadRange(offset, count int) ([]byte, error) 15 Write(offset int, value RegisterValue, size ebpf.Size) error 16 Size() int 17 Clone() Memory 18 } 19 20 // ValueMemory perserves type information, this is important for the functioning of the VM. Our VM implementation has no 21 // addessable memory slab in which pointers are assigned, rather in the type information we store pointers to actual 22 // memory blocks. Therefor, it is important to not lose type information when pointers are written to the stack. 23 type ValueMemory struct { 24 MemName string 25 Mapping []RegisterValue 26 } 27 28 func (vm *ValueMemory) Name() string { 29 return vm.MemName 30 } 31 32 func (vm *ValueMemory) Read(offset int, size ebpf.Size) (RegisterValue, error) { 33 if offset < 0 || offset+size.Bytes() > len(vm.Mapping) { 34 return nil, fmt.Errorf("attempt to read outside of memory bounds, off %d, size %d", offset, size.Bytes()) 35 } 36 37 val := vm.Mapping[offset] 38 for i := offset; i < offset+size.Bytes(); i++ { 39 // Since we store the RegisterValues not the actual bytes, programs are not allowed to read and combine 40 // the bytes of stored 2 values. This is likely incorrect behavior anyway. 41 if vm.Mapping[i] != val { 42 return nil, fmt.Errorf("indicated memory is not one contiguous value") 43 } 44 // TODO what should we do if the program reads only the upper or lower 32 bits of an 64 bit value for example? 45 // should we get the value and bit shift it? Or just error? 46 } 47 48 if val == nil { 49 return nil, fmt.Errorf("reading from non-initialized memory") 50 } 51 52 return val, nil 53 } 54 55 func (vm *ValueMemory) ReadRange(offset, count int) ([]byte, error) { 56 if offset < 0 || offset+count > len(vm.Mapping) { 57 return nil, fmt.Errorf("attempt to read range outside of memory bounds, off %d, count %d", offset, count) 58 } 59 60 r := make([]byte, count) 61 for i := 0; i < count; { 62 v := vm.Mapping[offset+i] 63 if v == nil { 64 r[i] = 0 65 i++ 66 continue 67 } 68 69 size := 1 70 for j := i + 1; j < i+8 && j < count; j++ { 71 if v != vm.Mapping[offset+j] { 72 break 73 } 74 size++ 75 } 76 77 // Round up to nearest valid value size 78 switch { 79 case size > 4: 80 binary.LittleEndian.PutUint64(r[i:i+8], uint64(v.Value())) 81 i += 8 82 case size > 2: 83 binary.LittleEndian.PutUint32(r[i:i+4], uint32(v.Value())) 84 i += 4 85 case size > 1: 86 binary.LittleEndian.PutUint16(r[i:i+2], uint16(v.Value())) 87 i += 2 88 default: 89 r[i] = byte(v.Value()) 90 i++ 91 } 92 } 93 94 return r, nil 95 } 96 97 func (vm *ValueMemory) Write(offset int, value RegisterValue, size ebpf.Size) error { 98 if offset < 0 || offset+size.Bytes() > len(vm.Mapping) { 99 return fmt.Errorf("attempt to read outside of memory bounds, off %d, size %d", offset, size.Bytes()) 100 } 101 102 for i := offset; i < offset+size.Bytes(); i++ { 103 vm.Mapping[i] = value 104 } 105 106 return nil 107 } 108 109 func (vm *ValueMemory) Clone() Memory { 110 clone := &ValueMemory{ 111 MemName: vm.MemName, 112 Mapping: make([]RegisterValue, len(vm.Mapping)), 113 } 114 copy(clone.Mapping, vm.Mapping) 115 return clone 116 } 117 118 func (vm *ValueMemory) Size() int { 119 return len(vm.Mapping) 120 } 121 122 // ByteMemory is a type of memory which is backed by a []byte with no type info, all values read will be IMM. 123 // Since the bytes may be directly loaded from ELF files with a byte order different from the host, reads and writes 124 // will happen according to the given byte order. 125 type ByteMemory struct { 126 MemName string 127 ByteOrder binary.ByteOrder 128 Backing []byte 129 } 130 131 func (bm *ByteMemory) Name() string { 132 return bm.MemName 133 } 134 135 func (bm *ByteMemory) Read(offset int, size ebpf.Size) (RegisterValue, error) { 136 if offset < 0 || offset+size.Bytes() > len(bm.Backing) { 137 return nil, fmt.Errorf("attempt to read outside of memory bounds, off %d, size %d", offset, size.Bytes()) 138 } 139 140 if bm.ByteOrder == nil { 141 bm.ByteOrder = binary.LittleEndian 142 } 143 144 var val int64 145 switch size { 146 case ebpf.BPF_B: 147 val = int64(bm.Backing[offset+0]) 148 case ebpf.BPF_H: 149 val = int64(bm.ByteOrder.Uint16([]byte{ 150 bm.Backing[offset+0], 151 bm.Backing[offset+1], 152 })) 153 case ebpf.BPF_W: 154 val = int64(bm.ByteOrder.Uint32([]byte{ 155 bm.Backing[offset+0], 156 bm.Backing[offset+1], 157 bm.Backing[offset+2], 158 bm.Backing[offset+3], 159 })) 160 case ebpf.BPF_DW: 161 val = int64(bm.ByteOrder.Uint64([]byte{ 162 bm.Backing[offset+0], 163 bm.Backing[offset+1], 164 bm.Backing[offset+2], 165 bm.Backing[offset+3], 166 bm.Backing[offset+4], 167 bm.Backing[offset+5], 168 bm.Backing[offset+6], 169 bm.Backing[offset+7], 170 })) 171 } 172 173 return newIMM(val), nil 174 } 175 176 func (bm *ByteMemory) ReadRange(offset, count int) ([]byte, error) { 177 if offset < 0 || offset+count > len(bm.Backing) { 178 return nil, fmt.Errorf("attempt to read range outside of memory bounds, off %d, count %d", offset, count) 179 } 180 181 r := make([]byte, count) 182 copy(r, bm.Backing[offset:]) 183 184 return r, nil 185 } 186 187 func (bm *ByteMemory) Write(offset int, value RegisterValue, size ebpf.Size) error { 188 if offset < 0 || offset+size.Bytes() > len(bm.Backing) { 189 return fmt.Errorf("attempt to read outside of memory bounds, off %d, size %d", offset, size.Bytes()) 190 } 191 192 if bm.ByteOrder == nil { 193 bm.ByteOrder = binary.LittleEndian 194 } 195 196 v := value.Value() 197 198 switch size { 199 case ebpf.BPF_B: 200 bm.Backing[offset] = byte(v) 201 case ebpf.BPF_H: 202 bm.ByteOrder.PutUint16(bm.Backing[offset:offset+2], uint16(v)) 203 case ebpf.BPF_W: 204 bm.ByteOrder.PutUint32(bm.Backing[offset:offset+4], uint32(v)) 205 case ebpf.BPF_DW: 206 bm.ByteOrder.PutUint64(bm.Backing[offset:offset+8], uint64(v)) 207 } 208 209 return nil 210 } 211 212 func (bm *ByteMemory) Clone() Memory { 213 clone := &ByteMemory{ 214 MemName: bm.MemName, 215 Backing: make([]byte, len(bm.Backing)), 216 } 217 copy(clone.Backing, bm.Backing) 218 return clone 219 } 220 221 func (bm *ByteMemory) Size() int { 222 return len(bm.Backing) 223 }