Live data from Hacker News

Stop Memsetting Structures

anmolsarma.in

121–130 of 277 posts

Re: Stop Memsetting Structures

#121
post #68
post #41

Earlier quoted context omitted.

Please explain how you send a struct over the network without UB and leaking the padding.

Use a packed struct (e.g. __attribute__((packed))). You may take a performance hit due to lack of alignment, but that's the judgement call

> performance hit due to lack of alignment

This isn't true of modern processors.

Re: Stop Memsetting Structures

#122
post #12

Earlier quoted context omitted.

Sending structures that include padding or that you don’t know the exact layout of over the network is another thing you shouldn’t do, period. Code doing that may break with a compiler change or a compilation flag change on one end of the connection, even if you can guarantee both ends use the same CPU.

RDMA (Infiniband) does DMA to/from network. Why we should not use RDMA?

Hmm, that concern is not just limited to RDMA; any kind of shared memory communication across a trust boundary is vulnerable to info leaks in the padding of the struct. That does seem like a real issue.

Not sure what's the appropriate mitigation. Having to memset is a drag, and it's easy to forget to do it in some place or another. I'm tempted to say that you should only use packed structs for shared memory communication across trust boundaries.

Re: Stop Memsetting Structures

#123
post #12
post #2

Does that also zero the padding that may exist between elements of the structure (or at its end)? Because if not, not memset'ing will open you up to all kinds of info leaks, especially if you plan to send that struct over the network (or to another process, or from kernel to userspace...). EDIT: Actually I have to apologize, because I’m not certain what the C standard says about whether the padding bytes can change w…

Sending structures that include padding or that you don’t know the exact layout of over the network is another thing you shouldn’t do, period. Code doing that may break with a compiler change or a compilation flag change on one end of the connection, even if you can guarantee both ends use the same CPU.

This kind of issue happens all the time with structs that a kernel, driver etc. expose to userspace: https://j00ru.vexillium.org/papers/2018/bochspwn_reloaded.pd...

Not sending the structure over the network is insufficient, and rigorous memset is a clear solution to this problem.

Also, C structure layout for a specific ABI is strictly determined; if that weren’t the case you couldn’t compile two C compilation units with different flags/compilers and expect them to work.

Re: Stop Memsetting Structures

#124
post #66
post #22

Earlier quoted context omitted.

My C++ is rusty (no pun intended), but if your code also has to compile as C++, I think this will work instead of memset: struct addrinfo hints = {0}; If C++ requires struct addrinfo hints = {}; instead, you could create a macro, and write struct addrinfo hints = ALL_ZEROES;

Sure. C++ has initializers ({1,2,3}), it just doesn't have C99 designated initializers ({.a = 1, .b = 2, .c = 3}). This is one of the main pain points around C++ not being a true superset of C.

G++ actually says “sorry” when it bails on that.

The other (bigger) pain point is that malloc returns void*, and C++ requires a cast.

Re: Stop Memsetting Structures

#125
post #7

Another reason to not memset structures: In the code struct foo { void * bar; } baz; ... memset(&baz, 0, sizeof(struct foo)); assert(baz.bar == NULL); it's possible for the assertion to fail, since NULL it not guaranteed to be represented in memory by a zero bytes.

That will never happen on current or future platforms, and is not a big concern. Or perhaps it'd be better to say that if it ever does happen on a future platform, then a few memset calls are going to be the least of your problems when porting legacy C code to that platform. However, what has bitten me is memsetting structures that I later turn into full-fledged classes in C++. Oops, there went the VMT. Designated in…

I do wish there was a compact way to tell a C++ class to zero init all pointer/numeric members to zero.

Re: Stop Memsetting Structures

#126

Earlier quoted context omitted.

> it's possible for the assertion to fail, since NULL it not guaranteed to be represented in memory by a zero bytes. Technically correct (which is of course the best kind of correct) but you'd be hard pressed to find a system in 2019 where NULL != (void*)0. The most recent machines with non-zero NULL in the C FAQ entry on the matter ( http://c-faq.com/null/machexamp.html ) date back to the mid '90s.

> you'd be hard pressed to find a system in 2019 where NULL != (void *)0 You'd be hard pressed to find a system at any time where NULL is not equal to a compile-time constant zero. ;-)

> The Prime 50 series used segment 07777, offset 0 for the null pointer,

BAM!!!

(All I had to do was click on the link in the comment you replied to, but the pedantry in this subthread is glorious, and I had to contribute — downvoters, this is your chance to school me on HN etiquette!)

Re: Stop Memsetting Structures

#127

Earlier quoted context omitted.

C++ programmers need to be converted. C programmers just need sympathy.

We don't really need sympathy, we get paid a lot and can afford to drown our sorrows.

At least until the next zero-day caused by a buffer overrun. (Kidding, kidding.)

Re: Stop Memsetting Structures

#128
post #86

Earlier quoted context omitted.

If you are processing data from the network, any uninitialized read gets attacked. My experience is padding (both inside a struct, and outside - array alignment for instance) gets attacked. The only safe option is to initialize everything . I say this having spent more than a decade working on software that is always being attacked.

You shouldn't be sending structs over the network, since their specific layout depends on the compiler (and version). At least make them packed if you're gonna do undefined behavior... But you should just do it the right way and have a wrapper that deals with endianness differences and such: struct S my_struct; // ... push_int64(&output_stream, &my_struct.value1); push_string(&output_stream, my_struct.value2); push_i…

The problem is not sending structs across the network.

The problem is processing any data from the network. You have to assume that data is malicious, and will attack any weakness.

I’m not talking about serialisation at all - I agreed entirely that sending struct padding across the net is less than optimal :)

Re: Stop Memsetting Structures

#129
post #2

Does that also zero the padding that may exist between elements of the structure (or at its end)? Because if not, not memset'ing will open you up to all kinds of info leaks, especially if you plan to send that struct over the network (or to another process, or from kernel to userspace...). EDIT: Actually I have to apologize, because I’m not certain what the C standard says about whether the padding bytes can change w…

Nope it doesn't: //GCC: -O0 //MSVC: /Od #include #include struct S { unsigned char x; int y; }; int f(int f) { S a; memset(&a, f, sizeof(a)); unsigned char y[2]; memcpy(&y, &a, sizeof(y)); return y[1]; } int g() { S a = { 1, 2 }; unsigned char y[2]; memcpy(&y, &a, sizeof(y)); return y[1]; } int main(int argc, char *argv[]) { f(0xDD); printf("%#x\n", g()); f(0xFF); printf("%#x\n", g()); } It definitely seems like the…

If you're relying upon the value of padding, you're already into undefined behavior.

Re: Stop Memsetting Structures

#130
post #103
post #64

Earlier quoted context omitted.

There's enough wiggle room in the standard that two compilers would be happy making two compliant but still different physical layouts for the same struct declaration. I'm not even worrying about cross-platform issues or bitfields yet. You can get away with directly writing a struct for simple structs, but eventually it will bite you.

Packed structs are reasonably standard (even gcc and visual studio can be made to pack in the same way using the same syntax these days). The remaining issue is endianness, but you can configure ARM to match x86 at boot so shrug . Also, if I tell any reasonably experienced developer my protocol sends a packed struct with these fields: uint8_t version uint16_t reserved uint8_t op int32_t status uint32_t data_length //…

My experience with packed structs is there really aren't any gotcha's. Either works or not.

The big annoyance is network people all use big endian probably just to spite everyone else.

Post reply on HN