"Tristan Mayfield" <[email protected]> writes: > Thank you to both Andrii and Toke! It's been extremely helpful to read > your responses. Having conversations like these really helps me when I > go into the source code and try to understand the overall intent of > it. I'm going to try and summarize the conversation to confirm my > understanding. > > bpf_probe_read() will read any valid kernel memory (nothing new here). > If the memory is already available to be read in the program (e.g. in > tracepoint args), then __builtin_memcpy can be used and will > potentially throw attach-time errors if reading structs incorrectly > (for some reason I don't think we clarified).
OK, I'll try to explain this one: Think of __builtin_memcpy() as a macro: it just compiles down to regular program instructions copying the memory (i.e., these two are roughly equivalent, modulo any optimisations the compiler might make): x = y; __builtin_memcpy(&x, &y, sizeof(x)); The verifier will check the resulting memory access instructions, to make sure you're not reading or writing out of bounds for whatever variable you're reading from / writing to. E.g., if you're reading from a context pointer, the verifier will know the size of the context object and make sure you only dereference up to the memory address ctx + sizeof(*ctx). > CO-RE can guarantee valid memory reads because of the nature of being > able to check offsets and relocations at load time rather than attach > time or just returning garbage data with no errors. Yes, that's basically what it boils down to. It works like this: What CO-RE does (for structs) is add some more information to the compiled binary so that you can reference struct members by name instead of memory offset. So, normally if you write: x = y->z; that will compile to a load from 'y + offsetof(typeof(y), z)', with the offset being computed at compile time. When you add the preserve_access_index attribute, clang will record a relocation that says you wanted the member named 'z' (and its type) by way of the BTF information. libbpf will read that at load time, and compute a new 'offsetof(typeof(y), z)' for the struct member as it exists in the running kernel, so that if the layout has changed, you'll still get the right offset. The load instruction in the byte code is then rewritten with this new offset. This means that by the time the bytecode is loaded into the kernel, it has already been rewritten, so the kernel bounds check is still the same - it'll just check that the memory you read is inside the size of the structure; but because the offsets have been fixed up, the end result you won't get out-of-bound errors - i.e., you might say that passing the bounds check is an implicit effect of the CO-RE rewriting. -Toke -=-=-=-=-=-=-=-=-=-=-=- Links: You receive all messages sent to this group. View/Reply Online (#1962): https://lists.iovisor.org/g/iovisor-dev/message/1962 Mute This Topic: https://lists.iovisor.org/mt/80853471/21656 Group Owner: [email protected] Unsubscribe: https://lists.iovisor.org/g/iovisor-dev/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
