Server Help

Non-Subspace Related Coding - #pragma pack(1) is the devil

Bak - Thu Jul 13, 2006 4:58 am
Post subject: #pragma pack(1) is the devil
Okay.... so I spent many hours debugging this bug, two entire coding sessions today. I'm pretty sure it's the most elusive bug I've found my coding career. Here's how it goes, three files main.cpp, header.h, and impl.cpp:

main.cpp:
Code: Show/Hide
#include <stdio.h>

#pragma pack(1)
#include "header.h"

void func()
{
   SampleStruct pm;

   printf("main, sizeof SampleStruct = %i\n",sizeof(SampleStruct));
}

int main()
{
   func();

   return 0;
}


header.h:
Code: Show/Hide
struct SampleStruct
{
   bool bool1;
   
   __int64 int1;

   SampleStruct();
};


impl.cpp:
Code: Show/Hide
#include "header.h"

#include <stdio.h>

SampleStruct::SampleStruct()
{
   int1 = bool1 = 0;

   printf("myplayer.cpp, sizeof struct = %i\n",sizeof(SampleStruct));
}


Seems innocent enough right? Well there's a buffer overflow in there, and if you run it, the code will crash when the execution leaves func() (assuming you have the same compiler and computer and compiler options as me). Here's the reason: Both main.cpp and impl.cpp use the SampleStruct. main uses #pragma pack(1), so it gives the struct exactly the number of bytes needed for the structure. In func(), only this number of bytes is reserved on the stack for variable pm.

In the constructor, which is in impl.cpp, the #pragma pack(1) is never seen so it pads the structure with extra space. Then when it writes values to the variables, it assumes they are packed. When main.cpp invokes the constructor, there is no padding on the stack. Bam, the constructor writes to memory that's not reserved for the structure, possibly overwriting the stack pointer, buffer overflow.

This bug is a bitch to track down as the compiler pads variables all the time, so overwriting a buffer a little bit might go unnoticed. For example if I change __int64 (a 64 bit variable on my computer) to an int (a 32 bit variable on my computer), the program executes fine. However, it prints out the different sizeof()'s for the same structure and you can see they are different. I compiled this using VS.NET2003.

So what the fuck? When you're using #pragma pack, make sure you push and pop the settings within the same file every time. Never leave an open ended pragma pack in a file, or include files within a pragma pack'd section of code. Anything less and you're asking to shoot yourself in the foot.

so in the standard asss distribution, defs.h include an open ended #pragma pack(1)... which leeches into everything that does an #include "asss.h" (every module). I think gcc by default ignores #pragma pack's, could this be the cause of some of asss' windows version instability (vs linux version)? I know it would be a bitch to trace if it was...
Cyan~Fire - Thu Jul 13, 2006 11:23 am
Post subject:
True, it's confusing, but it does make sense to me.

The simple solution is just to #pragma pack(push, 1) at the beginning of every include that needs it and #pragma pack(pop) at the end. I don't see what the big deal is.
Bak - Thu Jul 13, 2006 3:23 pm
Post subject:
Still might have #includes in the middle that use it.
Cyan~Fire - Fri Jul 14, 2006 12:02 am
Post subject:
As long as every file uses push and pop correctly, it should never be a problem.
All times are -5 GMT
View topic
Powered by phpBB 2.0 .0.11 © 2001 phpBB Group