I have a very old gameserver file for a video-game that became obsolete years ago (more than a decade and a half has passed since then). A lot of clones and reverse-engineered copycats of this engine exist, but nothing will ever be like the real thing, or at least without tons and tons of work (it's a MMORPG server). It's so old that it uses LinuxThreads instead of NPTL, and uses it for its thread-per-connection architecture (up to 1100 simmultaneos connections interacting with a huge 2D tilebased game world).
I was trying to rewrite it from scratch, since it has ancient GCC debug info available, but had to drop it since the amount of work to do was insane. Now I'm trying to inject new functionality into it, but couldn't proggress any further since I don't really understand how I'm supposed to inject into it.
I've tried coding a very simple injector using ptrace, but the server dies because of a segmentation fault (SIGSEGV) and since I can't attach GDB, I don't really know where the crash is happening.
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#define EMPTY_ADDR 0x80EF000UL
const char *SHELLCODE = "\x90\x90\x90\x90\x90\x90\x90\x90";
pid_t GamePID;
void GetGamePID() {
FILE *fp = fopen("/home/bob/lock.pid", "rb");
if (!fp) {
printf(":: [Error] Game not running.\n");
return;
}
fscanf(fp, "%d", &GamePID);
}
int main() {
GetGamePID();
if (!GamePID)
return 0;
if (ptrace(PTRACE_ATTACH, GamePID, NULL, NULL)) {
printf(":: [Error] Cannot attach.\n");
printf(":: # Fail %d: %s.\n", errno, strerror(errno));
return 0;
}
siginfo_t info;
waitid(P_PID, GamePID, &info, WSTOPPED);
printf(":: [Info] Attach on PID %d.\n", GamePID);
struct user_regs_struct oldregs, regs;
if (ptrace(PTRACE_GETREGS, GamePID, NULL, &oldregs)) {
printf(":: [Error] Cannot save registers (Fail %d).\n", errno);
ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
return 1;
}
unsigned long save[2];
save[0] = ptrace(PTRACE_PEEKTEXT, GamePID, (void *)(EMPTY_ADDR), NULL);
save[1] = ptrace(PTRACE_PEEKTEXT, GamePID, (void *)(EMPTY_ADDR + 8UL), NULL);
size_t payload_size = strlen(SHELLCODE);
uint64_t *payload = (uint64_t *)SHELLCODE;
printf("[*] Injecting payload at address 0x%lx.\n", EMPTY_ADDR);
for (size_t i = 0; i < payload_size; i += 8, payload++) {
if (ptrace(PTRACE_POKETEXT, GamePID, EMPTY_ADDR + i, *payload) < 0) {
printf(":: [Error] Failed to PTRACE_POKETEXT: %s\n", strerror(errno));
return 1;
}
}
regs = oldregs;
regs.rip = EMPTY_ADDR;
if (ptrace(PTRACE_SETREGS, GamePID, NULL, ®s)) {
printf(":: [Error] Cannot modify registers.\n");
ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
return 1;
}
if (ptrace(PTRACE_CONT, GamePID, NULL, NULL)) {
printf(":: [Error] Cannot run shellcode.\n");
ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
return 1;
}
waitid(P_PID, GamePID, &info, WSTOPPED);
// I used blank r-xp space but just in case...
if (ptrace(PTRACE_POKETEXT, GamePID, (void *)(EMPTY_ADDR), (void *)save[0]) ||
ptrace(PTRACE_POKETEXT, GamePID, (void *)(EMPTY_ADDR + 8UL), (void *)save[1])) {
printf(":: [Error] Cannot restore origcode.\n");
ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
return 1;
}
if (ptrace(PTRACE_SETREGS, GamePID, NULL, &oldregs)) {
printf(":: [Error] Cannot restore registers.\n");
ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
return 1;
}
if (ptrace(PTRACE_DETACH, GamePID, NULL, NULL)) {
printf(":: [Error] Cannot detach from process.\n");
return 1;
}
puts("[+] Done.");
}
That's how my injector looks like. Strings and whatnot is hardcoded because this is dummy code. Here is /proc/pid/maps output:
08047000-08048000 rwxp 00000000 08:01 549662 /home/bob/bin/game.orig
08048000-08125000 r-xp 00001000 08:01 549662 /home/bob/bin/game.orig
08125000-08129000 rwxp 000dd000 08:01 549662 /home/bob/bin/game.orig
08129000-0eb45000 rwxp 00000000 00:00 0
0f3e2000-14f10000 rwxp 00000000 00:00 0 [heap]
c573f000-f7b69000 rwxp 00000000 00:00 0
f7b6a000-f7b71000 rwxs 00000000 00:01 32821 /SYSV0000271b (deleted)
f7b71000-f7b72000 rwxp 00000000 00:00 0
f7bb5000-f7beb000 rwxp 00000000 00:00 0
f7bec000-f7d3e000 rwxp 00000000 00:00 0
f7d3e000-f7e57000 r-xp 00000000 08:01 1041510 /home/bob/lib/libc.so.6
f7e57000-f7e59000 r-xp 00119000 08:01 1041510 /home/bob/lib/libc.so.6
f7e59000-f7e5b000 rwxp 0011b000 08:01 1041510 /home/bob/lib/libc.so.6
f7e5b000-f7e5d000 rwxp 00000000 00:00 0
f7e5d000-f7e66000 r-xp 00000000 08:01 1041511 /home/bob/lib/libgcc_s.so.1
f7e66000-f7e67000 rwxp 00009000 08:01 1041511 /home/bob/lib/libgcc_s.so.1
f7e67000-f7e68000 rwxp 00000000 00:00 0
f7e68000-f7e8c000 r-xp 00000000 08:01 1041512 /home/bob/lib/libm.so.6
f7e8c000-f7e8d000 r-xp 00023000 08:01 1041512 /home/bob/lib/libm.so.6
f7e8d000-f7e8e000 rwxp 00024000 08:01 1041512 /home/bob/lib/libm.so.6
f7e8e000-f7f3d000 r-xp 00000000 08:01 1041514 /home/bob/lib/libstdc++.so.5
f7f3d000-f7f42000 rwxp 000ae000 08:01 1041514 /home/bob/lib/libstdc++.so.5
f7f42000-f7f47000 rwxp 00000000 00:00 0
f7f47000-f7f56000 r-xp 00000000 08:01 1041513 /home/bob/lib/libpthread.so.0
f7f56000-f7f57000 r-xp 0000e000 08:01 1041513 /home/bob/lib/libpthread.so.0
f7f57000-f7f58000 rwxp 0000f000 08:01 1041513 /home/bob/lib/libpthread.so.0
f7f58000-f7f9b000 rwxp 00000000 00:00 0
f7f9b000-f7f9e000 r--p 00000000 00:00 0 [vvar]
f7f9e000-f7f9f000 r-xp 00000000 00:00 0 [vdso]
f7f9f000-f7fb8000 r-xp 00000000 08:01 1041509 /home/bob/lib/ld-linux.so.2
f7fb8000-f7fb9000 r-xp 00018000 08:01 1041509 /home/bob/lib/ld-linux.so.2
f7fb9000-f7fba000 rwxp 00019000 08:01 1041509 /home/bob/lib/ld-linux.so.2
ff000000-ff001000 ---p 00000000 00:00 0
ff001000-ff200000 rwxp 00000000 00:00 0
ff200000-ff201000 ---p 00000000 00:00 0
ff201000-ff400000 rwxp 00000000 00:00 0
ff400000-ff401000 ---p 00000000 00:00 0
ff401000-ff600000 rwxp 00000000 00:00 0
ff600000-ff601000 ---p 00000000 00:00 0
ff601000-ff800000 rwxp 00000000 00:00 0
fff4b000-fffa2000 rwxp 00000000 00:00 0 [stack]
My server spawns each thread as an independent process, and as you can see I had to use my libraries (/home/bob/lib) copied from and ancient RedHat version which was compatible with the threading model (getpid() returns a different value for each thread).
I would really like to hear some suggestions on how I can inject new code. Is it just as hacky as writing directly to /proc/pid/mem? How would I override a game function (say, HandlePlayerMove) with /proc/pid/mem? Is it possible to inject a shared object using LD_PRELOAD given that it was compiled against a newer version of libc, libm, etc? Is there a way of knowing what's triggering SIGSEGV without ptrace failing?
edit: dummy code typo
Related
I came from this link: Force gdb to load shared library at randomized address and I learned that gdb will disable ASLR for the current process.
But the only way I know to disable ASLR is to do it globally via echo 0 > /proc/sys/kernel/randomize_va_space.
Now I'm wondering how does gdb disable ASLR on startup, and only for the current process?
EDIT
As ssbssa suggested, I wrote a program to test it:
#include <stdio.h>
#include <unistd.h>
#include <sys/personality.h>
int main(int argc, char **argv)
{
char *argv2[] = { argv[0], "test", "\0" };
if (argc == 1)
{
char *data = malloc(20);
printf("pid %d\n", getpid());
printf("heap allocated at %p\n", data);
printf("system() at %p\n", system);
puts("exit in 100s");
sleep(100);
exit(0);
}
personality(ADDR_NO_RANDOMIZE);
execvp(argv2[0], argv2);
}
And the process map is:
# cat /proc/1997932/maps
00400000-00401000 r--p 00000000 fc:01 430424 /root/no-aslr
00401000-00402000 r-xp 00001000 fc:01 430424 /root/no-aslr
00402000-00403000 r--p 00002000 fc:01 430424 /root/no-aslr
00403000-00404000 r--p 00002000 fc:01 430424 /root/no-aslr
00404000-00405000 rw-p 00003000 fc:01 430424 /root/no-aslr
01352000-01373000 rw-p 00000000 00:00 0 [heap]
7f222f928000-7f222f94a000 r--p 00000000 fc:01 394264 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f222f94a000-7f222fac2000 r-xp 00022000 fc:01 394264 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f222fac2000-7f222fb10000 r--p 0019a000 fc:01 394264 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f222fb10000-7f222fb14000 r--p 001e7000 fc:01 394264 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f222fb14000-7f222fb16000 rw-p 001eb000 fc:01 394264 /usr/lib/x86_64-linux-gnu/libc-2.31.so
I am new to mmap and still learning it . Based on my understanding i have created class for mmap which will be used to map a file in memory . The entire class is working fine but problem is coming when destructor is called ..the problem is segmentation fault or invalid pointer error is coming at the end of main function ...I have posted code of my class and main function which is using that class ...
Map.h
class MapData
{
public :
MapData();
~MapData();
MapData(char []);
bool OPEN();
void fnClose();
long fnGetSize();
char * fnGetFileRef();
char * ReadNextData(int );
private :
char * ptrRecord;
char ptrFileNm[250+1];
int fd;
struct stat sbuf;
bool bSuccess;
long sTotalSize;
char acData[2000+1];
long lCurrRead;
};
MapData.cpp
MapData::MapData()
{
}
MapData::~MapData()
{
printf("Inside MADATA Destructor \n ");
}
MapData::MapData(char acInput[])
{
strcpy(ptrFileNm,acInput);
sTotalSize=0;
lCurrRead=0;
bSuccess=false;
}
bool MapData::OPEN()
{
// if failed return false flg
if ((fd = open(ptrFileNm, O_RDONLY)) == -1) {
return bSuccess;
}
if (stat(ptrFileNm, &sbuf) == -1) {
return bSuccess;
}
// copy in local variable
sTotalSize = sbuf.st_size;
ptrRecord = (char * )mmap( (caddr_t)0, sTotalSize, PROT_READ, MAP_SHARED, fd, 0) ;
if (ptrRecord == (char *)(caddr_t)(-1) ) {
perror("Fail to Map Data ");
return bSuccess;
}
else
{
printf("Successfully Map Data***[%ld] \n",sTotalSize);
bSuccess=true;
}
return bSuccess;
}
char * MapData::fnGetFileRef()
{
return ptrRecord;
}
char * MapData::ReadNextData(int iX)
{
if((lCurrRead+iX)<sTotalSize)
{
memset(acData,0x00,sizeof(acData));
strncpy(acData,ptrRecord+lCurrRead,iX);
acData[iX+1]='\0';
lCurrRead+=iX;
}else{
strcpy(acData,"ZZZ");
}
return acData;
}
long MapData::fnGetSize()
{
return sTotalSize;
}
void MapData::fnClose()
{
// Don't forget to free the mmapped memory
if(munmap(ptrRecord, sTotalSize) == -1)
{
close(fd);
perror("Error un-mmapping the file");
exit(EXIT_FAILURE);
}
// Un-mmaping doesn't close the file, so we still need to do that.
close(fd);
printf("CLOSED SUCCESSFULLY \n ");
}
Main.cpp
int main()
{
char acFileNm[500+1];
MEMSET(acFileNm); // clean the variable
// file name to be read
strcpy(acFileNm,"ABDFILE.txt");
long lProcCnt=0; // no of byte read by program
char acLine[MAX_LINE_LENGTH+1]; // hold current read line
bool bFlag=true; // main flag
DEBUG_PRINT("File to be processed:%s \n",acFileNm);
// create object of mmap
MapData * pt1 = NULL;
pt1 = new MapData(acFileNm);
if(!pt1)
{
cout<<"Error creating object so quit ..."<<endl;
return 0 ;
}
auto_ptr<MapData> ptrMap( pt1 ); // pass ownership to auto deletor to delete memory
DEBUG_PRINT("STEP1:%s \n","OBJECT CREATION FOR FILE MAPPED IN MEMORY");
// try to open the file
if(ptrMap->OPEN())
{
// on success..get pointer to first char of file
char * ptrData = ptrMap->fnGetFileRef();
long lCompSize=ptrMap->fnGetSize(); // total no of bytes = fiexed line size * no of row + (no of row * EOL)
short int iEOL=0;
// logic to identify file generated on ewhich OS
if( (*(ptrData+MAX_LINE_LENGTH) == '\r') && (*(ptrData+MAX_LINE_LENGTH+1) == '\n'))
{
// DOS format CRLF
iEOL = 2;
}else if(*(ptrData+MAX_LINE_LENGTH) == '\n'){
// Unix format LF
iEOL = 1;
}
DEBUG_PRINT("STEP2: SIZEOFFILE%ld FILESYSTEM FORMAT:%d \n",lCompSize,iEOL);
// here read till it reaches maximum limit of file
while(lProcCnt<lCompSize)
{
//DEBUG_PRINT("PROC COUNTER[%ld] MAX_COUNTER[%ld] \n",lProcCnt,lCompSize);
lProcCnt+=MAX_LINE_LENGTH+iEOL; // increement no of bytes read at initial
MEMSET(acLine);
strncpy(acLine,ptrData+lProcCnt,MAX_LINE_LENGTH); // read line
acLine[MAX_LINE_LENGTH+1]='\0';
// process the line :function is called here to process the line
}
}else{
DEBUG_PRINT("MAP DATA FAILED OF FILE[%s] \n",acFileNm);
bFlag=false;
}
// at the end check if all the controls are matched
if(bFlag)
DEBUG_PRINT("END OF FILE PROCESSING SUCCESS \n");
else
DEBUG_PRINT("END OF FILE PROCESSING FAILED \n");
// close the memory map
ptrMap->fnClose();
MapData * ptr5 = ptrMap.release(); // release the ownership
delete ptr5; **// segmentation fault comes here ...**
}
Please suggest me where i am going wrong since gdb is also not helping ...detailed explanation will be good for me to understand ...
Stacktrace generated by gdb:
*** glibc detected *** DemoMap: free(): invalid pointer: 0x0804c000 ***
======= Backtrace: =========
/lib/libc.so.6[0x9bbc81]
/lib/libc.so.6[0x9be562]
/usr/lib/libstdc++.so.6(_ZdlPv+0x22)[0x544552]
DemoMap[0x80491e6]
/lib/libc.so.6(__libc_start_main+0xe6)[0x961d36]
DemoMap[0x8048d91]
======= Memory map: ========
00110000-00111000 r-xp 00000000 00:00 0 [vdso]
0044c000-00469000 r-xp 00000000 08:03 1237 /lib/libgcc_s-4.4.7-20120601.so.1
00469000-0046a000 rw-p 0001d000 08:03 1237 /lib/libgcc_s-4.4.7-20120601.so.1
00495000-00576000 r-xp 00000000 08:02 132841 /usr/lib/libstdc++.so.6.0.13
00576000-0057a000 r--p 000e0000 08:02 132841 /usr/lib/libstdc++.so.6.0.13
0057a000-0057c000 rw-p 000e4000 08:02 132841 /usr/lib/libstdc++.so.6.0.13
0057c000-00582000 rw-p 00000000 00:00 0
00929000-00947000 r-xp 00000000 08:03 1065 /lib/ld-2.12.so
00947000-00948000 r--p 0001d000 08:03 1065 /lib/ld-2.12.so
00948000-00949000 rw-p 0001e000 08:03 1065 /lib/ld-2.12.so
0094b000-00adb000 r-xp 00000000 08:03 1067 /lib/libc-2.12.so
00adb000-00adc000 ---p 00190000 08:03 1067 /lib/libc-2.12.so
00adc000-00ade000 r--p 00190000 08:03 1067 /lib/libc-2.12.so
00ade000-00adf000 rw-p 00192000 08:03 1067 /lib/libc-2.12.so
00adf000-00ae2000 rw-p 00000000 00:00 0
00b29000-00b51000 r-xp 00000000 08:03 1211 /lib/libm-2.12.so
00b51000-00b52000 r--p 00027000 08:03 1211 /lib/libm-2.12.so
00b52000-00b53000 rw-p 00028000 08:03 1211 /lib/libm-2.12.so
08048000-0804b000 r-xp 00000000 08:08 2883976 DemoMap
0804b000-0804c000 rw-p 00002000 08:08 2883976 DemoMap
0804c000-0806d000 rw-p 00000000 00:00 0 [heap]
b7e00000-b7e21000 rw-p 00000000 00:00 0
b7e21000-b7f00000 ---p 00000000 00:00 0
b7f9b000-b7fe5000 r--s 00000000 08:08 4326707 ABCDEF.TXT
b7fe5000-b7fe8000 rw-p 00000000 00:00 0
b7ffd000-b8000000 rw-p 00000000 00:00 0
bffeb000-c0000000 rw-p 00000000 00:00 0 [stack]
may be here.
char ptrFileNm[250+1];
char acFileNm[500+1];
MapData::MapData(char acInput[])
{
strcpy(ptrFileNm,acInput);//segmentation fault
sTotalSize=0;
lCurrRead=0;
bSuccess=false;
}
I have one problem. I am trying to get stack offset range from /proc/self/maps pseudofile. But I have sometime weird things.
Here is my code
fp = fopen("/proc/self/maps", "r");
if (fp == NULL) {
perror("Error opening file");
return NULL;
}
while (fgets(line, 2048, fp) != NULL) {
if (strstr(line, "stack") != NULL) {
printf("%s", line);
}
}
If you start the programm with one or multiple thread you can view this pseudo file and get something like this
7f20423a6000-7f2042ba6000 rw-p 00000000 00:00 0 [stack:3936]
7fffbe95e000-7fffbe97f000 rw-p 00000000 00:00 0 [stack]
the first line here is stack of thread , the second line is the stack of the process.
But the problem is that sometimes I cannot get stack of thread. It can be from the first time or appear on some next execution so it is not determined. On some distos it doesn't show stack of thread at all, I don't think the problem is in different implementation of pseudo file in distros but in something other.
Please help to solve this problem
EDIT
I actually call this function inside thread , so I create thread through pthread_create(&tid, NULL, proc_stack, NULL);I have been also thinking about this. Maybe it needs some time to update this pseudofile after thread start, this is only one reason I see here.
EDIT2
I've tried to call sleep forcely , but this didn't help, but the most weird is that on one distro it shows thread stack, on another doesn't.
On my system Your program also dosn't show second stack segment, but it seems 5 segments are allocated above [heap] and one of them is used for thread stack (local variables of proc_stack() are stored in this segment).
Code I used to check for it (test.c):
#include <stdio.h>
#include <string.h>
#include <pthread.h>
void* proc_stack(void* p){
FILE *fp;
char line[2048];
fp = fopen("/proc/self/maps", "r");
if (fp == NULL) {
perror("Error opening file");
return NULL;
}
while (fgets(line, 2048, fp) != NULL) {
// if (strstr(line, "stack") != NULL) {
printf("%s", line);
// }
}
printf("addr = %p %p\n", &fp, &line);
return NULL;
}
int main(){
pthread_t tid;
void *rv;
proc_stack( NULL );
puts("main");
pthread_create( &tid, NULL, proc_stack, NULL );
pthread_join( tid, &rv );
return 0;
}
Compiled with gcc -Wall test.c -pthread. Results (some lines removed):
...
01933000-01954000 rw-p 00000000 00:00 0 [heap]
...
7fff75be3000-7fff75c04000 rw-p 00000000 00:00 0 [stack]
7fff75d97000-7fff75d98000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
addr = 0x7fff75c00e68 0x7fff75c00e70
main
...
01933000-01954000 rw-p 00000000 00:00 0 [heap]
7f3be0000000-7f3be0021000 rw-p 00000000 00:00 0
7f3be0021000-7f3be4000000 ---p 00000000 00:00 0
7f3be6b79000-7f3be6b7a000 rw-p 00000000 00:00 0
7f3be6b7a000-7f3be6b7b000 ---p 00000000 00:00 0
7f3be6b7b000-7f3be737b000 rw-p 00000000 00:00 0
...
7fff75be3000-7fff75c04000 rw-p 00000000 00:00 0 [stack]
7fff75d97000-7fff75d98000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
addr = 0x7f3be73796c8 0x7f3be73796d0
I have been looking around at the meaning of this error and it seems to mean that I am freeing the same object more than once. I can't seem to figure out how to prevent this. Any help or suggestions would be much appreciated.
File(randname)
File(a.out)
~File(a.out)
~Directory(randname)
~File(null)
~File()
*** glibc detected *** a.out: double free or corruption (fasttop): 0x0804b048 ***
======= Backtrace: =========
/lib/libc.so.6(+0x6ff0b)[0xb74baf0b]
/usr/local/gcc/gcc-cilk/lib/libstdc++.so.6(_ZdlPv+0x1f)[0xb7671b4f]
/usr/local/gcc/gcc-cilk/lib/libstdc++.so.6(_ZdaPv+0x1b)[0xb7671b9b]
a.out[0x8048983]
a.out[0x8048b12]
a.out[0x80487d7]
/lib/libc.so.6(__libc_start_main+0xf3)[0xb7464003]
a.out[0x8048701]
======= Memory map: ========
08048000-08049000 r-xp 00000000 00:25 268562602 /home/user/test/a.out
08049000-0804a000 r--p 00000000 00:25 268562602 /home/user/test/a.out
0804a000-0804b000 rw-p 00001000 00:25 268562602 /home/user/test/a.out
0804b000-0806c000 rw-p 00000000 00:00 0 [heap]
b7448000-b744b000 rw-p 00000000 00:00 0
b744b000-b75b2000 r-xp 00000000 08:01 1365267 /lib/libc-2.14.1.so
b75b2000-b75b4000 r--p 00167000 08:01 1365267 /lib/libc-2.14.1.so
b75b4000-b75b5000 rw-p 00169000 08:01 1365267 /lib/libc-2.14.1.so
b75b5000-b75b8000 rw-p 00000000 00:00 0
b75b8000-b75d3000 r-xp 00000000 08:01 1179017 /usr/local/gcc/gcc-cilk/lib/libgcc_s.so.1
b75d3000-b75d4000 r--p 0001a000 08:01 1179017 /usr/local/gcc/gcc-cilk/lib/libgcc_s.so.1
b75d4000-b75d5000 rw-p 0001b000 08:01 1179017 /usr/local/gcc/gcc-cilk/lib/libgcc_s.so.1
b75d5000-b75fe000 r-xp 00000000 08:01 1365275 /lib/libm-2.14.1.so
b75fe000-b75ff000 r--p 00028000 08:01 1365275 /lib/libm-2.14.1.so
b75ff000-b7600000 rw-p 00029000 08:01 1365275 /lib/libm-2.14.1.so
b7622000-b7624000 rw-p 00000000 00:00 0
b7624000-b770b000 r-xp 00000000 08:01 1179021 /usr/local/gcc/gcc-cilk/lib/libstdc++.so.6.0.19
b770b000-b770f000 r--p 000e7000 08:01 1179021 /usr/local/gcc/gcc-cilk/lib/libstdc++.so.6.0.19
b770f000-b7710000 rw-p 000eb000 08:01 1179021 /usr/local/gcc/gcc-cilk/lib/libstdc++.so.6.0.19
b7710000-b7718000 rw-p 00000000 00:00 0
b7718000-b7737000 r-xp 00000000 08:01 1365260 /lib/ld-2.14.1.so
b7737000-b7738000 r--p 0001f000 08:01 1365260 /lib/ld-2.14.1.so
b7738000-b7739000 rw-p 00020000 08:01 1365260 /lib/ld-2.14.1.so
bfa18000-bfa39000 rw-p 00000000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
Aborted
And here is the code:
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
class File
{
protected:
unsigned char recordLen;
unsigned location;
unsigned fileSize;
unsigned char filenameLen;
char* filename;
public:
File(){filename = NULL;}
File(FILE* readFromHere, const char* name)
{
recordLen = 152;
location = 20003;
fileSize = 16348;
filenameLen = strlen(name);
filename = new char[filenameLen + 1];
strcpy(filename, name);
cout << "File(" << filename << ")\n";
}
File(const File& o)
{
if (o.filename == NULL)
filename = o.filename;
else
{
filename = new char[o.filenameLen + 1];
strcpy(filename, o.filename);
}
}
~File()
{
if (filename)
cout << "~File(" << filename << ")\n";
else
cout << "~File(null)\n";
if (filename != NULL)
delete[] filename;
}
};
class Directory : public File
{
protected:
int numContents;
File* contents;
public:
Directory(FILE* readFromHere, const char* name)
: File(readFromHere, name)
{
numContents = 2;
contents = new File[numContents];
contents[0] = File(readFromHere, "a.out");
//~ contents[1] = File(readFromHere, "otherfile.cpp");
}
~Directory()
{
if (filename)
cout << "~Directory(" << filename << ")\n";
else
cout << "~Directory(null)\n";
if (contents != NULL)
delete[] contents;
}
};
int main()
{
Directory d(NULL, "randname");
sleep(2);
return 0;
}
Your class doesn't follow the Rule of Three even though it owns resources. You have a copy constructor and a destructor, but not a copy assignment operator. Which means that this line:
contents[0] = File(readFromHere, "a.out");
invokes the default copy assignment operator, which happily copies pointers. There's your double-deletion problem.
i have the following main code:
#include "ComHandler.h"
#include "socketMessage.h"
#define THIS_IP "localhost"
#define C_PORT 32456
#define S_PORT 25465
#define S_PORT_UDP 22548
int main(int argc, char* argv[]) {
conOptions opts;
strncpy(opts.password, "PASS", sizeof("PASS"));
opts.retryQty = 5;
opts.timeout = 300;
char dir[INET6_ADDRSTRLEN];
strncpy(dir, THIS_IP, sizeof(THIS_IP));
if (argv[1][0] == 'C') {
opts.connType = CLIENT_HANDLER;
opts.thisID = 2;
ComHandler comH(opts);
char buffer[10];
strncpy(buffer, "Testing", 10);
int rtnValue = comH.sendMsg(buffer, 10, 1, TCP_C);
std::cout << "Returned Value: " << rtnValue << std::endl;
} else {
opts.connType = SERVER_HANDLER;
opts.thisID = 1;
ComHandler comH(opts);
comH.addConnectionData(2, dir, C_PORT);
comH.addConnectionData(comH.getID(), dir, S_PORT);
char rcvdTxt[MINIDATA_DATA_SIZE];
comH.receiveMsg(rcvdTxt, MINIDATA_DATA_SIZE, 2, TCP_C);
std::cout << "Received: " << rcvdTxt << std::endl;
comH.closeCommunications();
}
}
The code is for testing my communication platform. The platform works well, but the problem appears when the messages are received by the Handler (ComHandler). When i call the method "receiveMsg" from ComHandler, i get a "stack smashing detected" in the "return" of the method.
This is the "ComHandler" class:
#ifndef COMHANDLER_H_
#define COMHANDLER_H_
#include "Constants.h"
#include "ComConstants.h"
#include "DataLogger.h"
#include <list>
#include <math.h>
#define CLIENT_HANDLER 1
#define SERVER_HANDLER 2
#define COMMENT_LINE '#'
#define TXT_SEPARATOR ';'
#define MINIDATA_DATA_SIZE (COMDATA_MAX_SIZE - 4* sizeof(int))
enum CON_TYPE {
TCP_C, UDP_C
};
#pragma pack(push)
#pragma pack(1)
typedef struct {
int totalPackets;
int sequenceNumber;
int sequenceID;
int dataSize;
char data[MINIDATA_DATA_SIZE];
} miniData;
#pragma pack(pop)
typedef struct connectionOptions {
unsigned int timeout;
unsigned int retryQty;
id_t thisID;
char password[PASSWORD_MAX_SIZE];
int connType;
connectionOptions() :
timeout(CON_TIMEOUT), retryQty(SOCKET_RETRIES), connType(
SERVER_HANDLER) {
memset(password, 0, PASSWORD_MAX_SIZE);
}
connectionOptions(connectionOptions& opt) {
timeout = opt.timeout;
retryQty = opt.retryQty;
thisID = opt.thisID;
connType = opt.connType;
memcpy(password, opt.password, PASSWORD_MAX_SIZE);
}
} conOptions;
class ComHandler {
private:
int dataForwarder_ReceiverMsgQueue;
int dataForwarder_SenderMsgQueue;
conOptions opts;
int sequenceID;
Semaphore ch_mux;
SharedMemory<comHandlerGlOp> ch_shm;
void cpyMiniData(miniData* to, miniData* from);
void createIPCs();
public:
ComHandler();
ComHandler(conOptions options);
ComHandler(id_t thisID);
virtual ~ComHandler();
int receiveMsg(char* buffer, int bufferSize, id_t originID,
CON_TYPE type = TCP_C);
int sendMsg(char* buffer, std::size_t bufferSize, id_t destinationID,
CON_TYPE type = TCP_C);
void configListenersData(char addr[INET6_ADDRSTRLEN], int port,
CON_TYPE type);
void addConnectionData(id_t id, char addr[INET6_ADDRSTRLEN], int port);
void addConnectionData(const char* file);
void modifyConnectionData(id_t id, char addr[INET6_ADDRSTRLEN], int port);
void removeConnectionData(id_t id);
int getID();
void closeCommunications();
};
#endif /* COMHANDLER_H_ */
The comData structure:
typedef struct {
long type;
int requestType;
char destination[INET6_ADDRSTRLEN];
char origin[INET6_ADDRSTRLEN];
id_t originID;
id_t destinationID;
int port;
pid_t senderPid;
pid_t originPid;
char data[COMDATA_MAX_SIZE];
} comData;
And the method with the problem:
int ComHandler::receiveMsg(char* buffer, int bufferSize, id_t originID,
CON_TYPE type) {
comData aux;
aux.type = DATA_RECEIVE;
int qtyReceived = 0;
if (originID != ANY_MSG_ORIGIN) {
if (type == TCP_C)
aux.requestType = RECEIVE_DATA_FROM_DESTINATION_TCP;
else
aux.requestType = RECEIVE_DATA_FROM_DESTINATION_UDP;
} else {
if (type == TCP_C)
aux.requestType = RECEIVE_ANY_DATA_TCP;
else
aux.requestType = RECEIVE_ANY_DATA_UDP;
}
aux.originID = originID;
aux.senderPid = getpid();
aux.originPid = getpid();
msgsnd(dataForwarder_ReceiverMsgQueue, &aux, sizeof(comData), 0);
msgrcv(dataForwarder_ReceiverMsgQueue, &aux, sizeof(comData), getpid(), 0);
/* More Code */
return qtyReceived;
}
The stack smashing error appears after "return qtyReceived;" is called. And the /* More Code */ in the middle is the non important code, because if a delete that part, the stack smashing stills appears.
After much debugging i found that the smashing appears if i call:
`msgrcv(dataForwarder_ReceiverMsgQueue, &aux, sizeof(comData), getpid(), 0);`
if i comment that line, i don't get the error.
Also with the debugger, i was able to see that the messages from the queue arrive with good format and size, exactly as they should.
The console output is:
*** stack smashing detected ***: /media/blackhole/workspace/Final/bin/ComTest terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x45)[0xb7df88d5]
/lib/i386-linux-gnu/libc.so.6(+0xe7887)[0xb7df8887]
/media/blackhole/workspace/Final/bin/ComTest[0x804dc6b]
/media/blackhole/workspace/Final/bin/ComTest[0x804a002]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb7d2a113]
/media/blackhole/workspace/Final/bin/ComTest[0x8049d11]
======= Memory map: ========
08048000-08054000 r-xp 00000000 08:05 256373 /media/blackhole/workspace/Final/bin/ComTest
08054000-08055000 r--p 0000b000 08:05 256373 /media/blackhole/workspace/Final/bin/ComTest
08055000-08056000 rw-p 0000c000 08:05 256373 /media/blackhole/workspace/Final/bin/ComTest
08056000-08077000 rw-p 00000000 00:00 0 [heap]
b7d0f000-b7d11000 rw-p 00000000 00:00 0
b7d11000-b7e87000 r-xp 00000000 08:06 525221 /lib/i386-linux-gnu/libc-2.13.so
b7e87000-b7e89000 r--p 00176000 08:06 525221 /lib/i386-linux-gnu/libc-2.13.so
b7e89000-b7e8a000 rw-p 00178000 08:06 525221 /lib/i386-linux-gnu/libc-2.13.so
b7e8a000-b7e8d000 rw-p 00000000 00:00 0
b7e8d000-b7ea9000 r-xp 00000000 08:06 525242 /lib/i386-linux-gnu/libgcc_s.so.1
b7ea9000-b7eaa000 r--p 0001b000 08:06 525242 /lib/i386-linux-gnu/libgcc_s.so.1
b7eaa000-b7eab000 rw-p 0001c000 08:06 525242 /lib/i386-linux-gnu/libgcc_s.so.1
b7eab000-b7eac000 rw-p 00000000 00:00 0
b7eac000-b7ed4000 r-xp 00000000 08:06 525251 /lib/i386-linux-gnu/libm-2.13.so
b7ed4000-b7ed5000 r--p 00028000 08:06 525251 /lib/i386-linux-gnu/libm-2.13.so
b7ed5000-b7ed6000 rw-p 00029000 08:06 525251 /lib/i386-linux-gnu/libm-2.13.so
b7ed6000-b7fb4000 r-xp 00000000 08:06 5514 /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7fb4000-b7fb5000 ---p 000de000 08:06 5514 /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7fb5000-b7fb9000 r--p 000de000 08:06 5514 /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7fb9000-b7fba000 rw-p 000e2000 08:06 5514 /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7fba000-b7fc1000 rw-p 00000000 00:00 0
b7fdc000-b7fdd000 rw-s 00000000 00:04 163119127 /SYSV02056f32 (deleted)
b7fdd000-b7fdf000 rw-p 00000000 00:00 0
b7fdf000-b7fe0000 r-xp 00000000 00:00 0 [vdso]
b7fe0000-b7ffe000 r-xp 00000000 08:06 525208 /lib/i386-linux-gnu/ld-2.13.so
b7ffe000-b7fff000 r--p 0001d000 08:06 525208 /lib/i386-linux-gnu/ld-2.13.so
b7fff000-b8000000 rw-p 0001e000 08:06 525208 /lib/i386-linux-gnu/ld-2.13.so
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
So, any ideas? If you need something about the code, just ask me.
According to this, the msgsz parameter of msgrcv() should be the size of the mtext member of the mymsg struct:
struct mymsg {
long int mtype; /* message type */
char mtext[1]; /* message text */
}
The problem is that you include the size of the mtype member (4 bytes) so msgrcv() writes four bytes past the end of your struct which trashes the stack.