2

While reading the CERT C Coding Standard, I came across DCL39-C which discusses why it's generally a bad idea for something like the Linux kernel to return an unpacked struct to userspace due to information leakage.

In a nutshell, structs aren't generally packed by default and the padding bytes between members of a struct often contain uninitialized data, hence the information leakage.

Why aren't structs packed by default? There was a mention in the guide that it's an optimization feature of compilers for specific architectures, I believe. Why is aligning structs to a certain byte size more efficient, as it wastes memory space?

Also, why doesn't the C standard specify a standardized way of asking for a packed struct? I can ask GCC using __attribute__((packed)), and there are other ways for different compilers, but it seems like a feature that'd be nice to have as part of the standard.

Naftuli Kay
  • 87,710
  • 93
  • 269
  • 411
  • 5
    Unaligned accesses take longer. – Ignacio Vazquez-Abrams Jan 31 '16 at 04:13
  • 4
    And they don't work on all architectures. – rici Jan 31 '16 at 04:14
  • Related: http://stackoverflow.com/q/8568432/827263 – Keith Thompson Jan 31 '16 at 04:31
  • @rici There's no inherent reason a compiler implementing a packed struct couldn't also emit the necessary code to access a "misaligned" member on an architecture where unaligned word access doesn't work. – Random832 Jan 31 '16 at 04:33
  • 2
    @Random832: `void f(int* vp) { static sum = 0; *vp += sum; sum = *vp;}` Now, elsewhere where the definition of f is not known: `int *valp = &packed->unaligned_int; f(valp);` – rici Jan 31 '16 at 04:55
  • @rici it could forbid taking the address of a member of a packed struct, or have the pointer be a different type qualified with the fact that it may be unaligned (access to which would generate the same sort of code). Or it could do it for _all_ accesses through pointers. – Random832 Jan 31 '16 at 15:13
  • 1
    @Random832: your first two options define a different language so I don't think they can sustain the claim that there is "no inherent reason". The last option is indeed possible; the compiler could insert code to check alignment for *every* pointer access. But would anyone actually use such a language, much less to write an operating system kernel? – rici Jan 31 '16 at 17:28
  • @rici I was assuming that there would be some nonstandard attribute specifying the structures would be packed in the first case, I'd lost track of the context of the question. – Random832 Jan 31 '16 at 17:30
  • https://stackoverflow.com/questions/38875369/what-is-data-alignment-why-and-when-should-i-be-worried-when-typecasting-pointe – M.M Dec 22 '20 at 23:58

3 Answers3

1

Data is carried though electronic circuits by groups of parallel wires (buses). Likewise, the circuits themselves tend to be arrayed in parallel. The physical distance between parallel components adds resistance and capacitance to any crosswise wires that bridge them. So, such bridges tend to be expensive and slow, and computer architects avoid them when possible.

Unaligned loads require shifting bytes across lanes. Some CPUs (e.g. efficiency-oriented RISC) are physically incapable of doing this, because the bridge component doesn't exist. Some will detect the condition and interpose a lane shift at the expense of a cycle or two. Others can handle misalignment without a speed penalty… assuming paged memory doesn't add another problem.

There's another, completely different issue. The memory management unit (MMU) sits between the CPU execution core and the memory bus, translating program-visible logical addresses to the physical addresses for memory chips. Two adjacent logical addresses might reside on different chips. The MMU is optimized for the common case where an access only requires one translation, but a misaligned access may require two.

A misaligned access straddling a page boundary might incur an exception, which might be fatal inside a kernel. Since pages are relatively large, this condition is relatively rare. It might evade tests, and it may be non-deterministic.

TL;DR: Packed structures shouldn't be used for active program state, especially not in a kernel. They may be used for serialization and communication, but safe usage is another question.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
1

Leaving structs "unpacked" allows the compiler to align all members so that operations are more efficient on those members (measured in terms of clock time, number of instructions, etc). The alignment requirement for types depends on the host architecture and (for struct types) on the alignment requirement of contained members.

Packing struct members forces some (if not all) members to be aligned in a way that is sub-optimal for performance. In some worst cases - depending on host architecture - operations on unaligned variables (or on unaligned struct members) triggers a processor fault condition. RISC processor architectures, for example, generate an alignment fault when a load or store operation affects an unaligned address. Some SSE instructions on recent x86 architectures require data they act on to be aligned on 16 byte boundaries.

In best cases, the operations behave as intended, but less efficiently, due to overhead of copying an unaligned variable to an aligned location or to a register, doing the operation there, and copying it back. Those copying operations are less efficient when unaligned variables are involved - after all, the processor architecture is optimised for performance when variable alignment meets its design requirements.

If you are worried about data leaking out of your program, simply use functions like memset() to overwrite the contents of your structures at the end of their lifetime (e.g. just before an instance is about to pass out of scope, or immediately before dynamically allocated memory is deallocated using free()).

Or use an operating system (like OpenBSD) which does overwrite memory before making it available to processes or programs. Bear in mind that such features tend to make both the operating system and programs it hosts run less efficiently.

Recent versions of the C standard (since 2011) do have some facilities to query and control alignment of variables (and affect packing of struct members). The default is whatever alignment is most effective for the host architecture - which for struct types normally means unpacked.

Peter
  • 35,646
  • 4
  • 32
  • 74
0

On some compilers such as Microchip XC8, all structs are indeed always packed.

On some platforms compilers will only generate byte access instructions to access members of a packed struct, because byte access instructions are always aligned. If all structs are packed, the 16-, 32-, and 64- bit load/store instructions are not used. This is an obvious waste of resources.

The C standard does not specify a way of packing struct possibly because the standard itself is not aware of the concept of packing. Since the layout of non-bit-field members of structs is implementation defined, out of scope for the standard. Or possibly, the standard is made to support architectures that always add padding in structs, since such architectures are indeed theoretically feasible.