• C

Segmentation Fault

I am parsing a TCPDUMP file (in HEX) and when I try and print certain fileds like src and dst address get a segmentation fault.  Below is from GDB debug and shows value of my struct IP in memory at the time as well the the line of code that is faulting.  Why??

    Program received signal SIGSEGV, Segmentation fault.
0x00010e4c in main (argc=2, argv=0xffbffa04) at change.c:71
71            printf("SrcIP : %s\n", inet_ntoa(ip->ip_src));

 (gdb) print *ip
$1 = {ip_v = 4 '\004', ip_hl = 5 '\005', ip_tos = 0 '\0', ip_len = 52,
  ip_id = 14200, ip_off = 16384, ip_ttl = 128 '\200', ip_p = 6 '\006',
  ip_sum = 24303, ip_src = {S_un = {S_un_b = {s_b1 = 172 '¬',
        s_b2 = 31 '\037', s_b3 = 11 '\v', s_b4 = 23 '\027'}, S_un_w = {
        s_w1 = 44063, s_w2 = 2839}, S_addr = 2887715607}}, ip_dst = {S_un = {
      S_un_b = {s_b1 = 172 '¬', s_b2 = 31 '\037', s_b3 = 1 '\001',
        s_b4 = 7 '\a'}, S_un_w = {s_w1 = 44063, s_w2 = 263},
      S_addr = 2887713031}}}
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Could you post more of your code areound line 71? In particular how you allocate and initialize 'ip'
jma2000Author Commented:
ok here it is:  Thx

/* If we have an IP packet... */
    if (ntohs(eth_header_ptr->ether_type) == EtherType_IP)
      /* Get the IP header */
      struct ip            *ip;                    /* The IP header */
      printf ("size %d\n",size_ethernet);
     ip = (struct ip*)(pktbuf + size_ethernet);

     printf("     Length = %u\n", ip->ip_len);
     printf("     IP ID = %u\n", ip->ip_id);
     printf("     Protocol = %x\n", ip->ip_p);
      /* Print some info for testing */
      printf("SrcIP : %c\n", inet_ntoa(ip->ip_src));
      printf("DstIP : %s\n", inet_ntoa(ip->ip_dst));
Are you sure that shouldn't be

      printf("SrcIP : %s\n", inet_ntoa(ip->ip_src)); // not '%c'
      printf("DstIP : %s\n", inet_ntoa(ip->ip_dst));
IT Degree with Certifications Included

Aspire to become a network administrator, network security analyst, or computer and information systems manager? Make the most of your experience as an IT professional by earning your B.S. in Network Operations and Security.

jma2000Author Commented:
Sorry..I was exprimenting.  %c  aso caused a fault but so does %s as you can see in the above GDB trace.  
What's the declaration of 'IP'?
jma2000Author Commented:
if it helps this is the struct ip from #include <netinet/ip.h>

struct ip {
 49         u_int   ip_hl:4,                /* header length */
 50                 ip_v:4;                 /* version */
 51 #endif
 53         u_int   ip_v:4,                 /* version */
 54                 ip_hl:4;                /* header length */
 55 #endif
 56         u_char  ip_tos;                 /* type of service */
 57         u_short ip_len;                 /* total length */
 58         u_short ip_id;                  /* identification */
 59         u_short ip_off;                 /* fragment offset field */
 60 #define IP_RF 0x8000                    /* reserved fragment flag */
 61 #define IP_DF 0x4000                    /* dont fragment flag */
 62 #define IP_MF 0x2000                    /* more fragments flag */
 63 #define IP_OFFMASK 0x1fff               /* mask for fragmenting bits */
 64         u_char  ip_ttl;                 /* time to live */
 65         u_char  ip_p;                   /* protocol */
 66         u_short ip_sum;                 /* checksum */
 67         struct  in_addr ip_src,ip_dst;  /* source and dest address */
 68 } __packed;
After looking at that for a while, it seems OK. Are you compiling with any optimizations enabled? (BTW, I assume that 'Length' etc. are outputted correctly)
jma2000Author Commented:
Yes length, ID, Protocol print correctly but none of the other elements do: for example
printf("     IP version = %d\n", ip_buffer->ip_v); also faults.

compile is gcc - o foo foo.c -lsocket -lnsl

I am going nuts over this as it all looks ok
What is the value of size_ethernet?
Does the problem happening the first time or after some parsings?
jma2000Author Commented:
size_ethernet = 14,

It processes the ethernet headers fine but as soon it it sees an IP protocol and tries to parse the IP fields it faults on most fields (not on length, protocol)
Is pktbuf unsiged char?
jma2000Author Commented:
Here is how pktbuf is defined:

char *pktbuf

pktbuf = malloc(frame_header.bytes_caped + 1);
What is the CPU?   Some CPU's can't access words at odd addresses.

Try to define pktbuf as
unsigned char *pktbuf;

jma2000Author Commented:
It is a Sun solaris box - tried it on oa few of them.

Unsigned char didn't seem to help.

Is it related to the type of format the data is stored in  - it is in HEX not ASCII.
I was looking at the netinet/ip.h, and the structure is defined inside a
#ifdef __USE_BSD
which is defined on features.h by defining the following line on the code
#define _BSD_SOURCE 1
or compile using -D_BSD_SOURCE

This came from my linux box. I don't know if its the same thing on Solaris. You can try this as well.
I wrote this small program to write an ip header to a file in binary format (unsigned char), read it back and print the source and destination ip addresses.
It works fine for me. You can test it in your environment.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <netinet/tcp.h>

int main()
    FILE *f;
    struct ip *ip;
    unsigned char str[]= { 0x45,0x00,0x00,0x3c,0xeb,0xc0,0x40,0x00,0x40,0x06,0xfc,0x03,
          0xac,0x1a,0x1c,0x61,    /*Source IP*/
          0xac,0x1a,0x1c,0xa2 };  /* Destination IP*/
    unsigned char *strl;

    strl = (unsigned char*) malloc (50);

    /* create the file of binary file with the ip header */
    fwrite(&str,sizeof(struct ip),1,f);

    /* read the ipheader */
    fread(strl,sizeof(struct ip),1,f);
    ip = (struct ip*) strl;
    printf("SrcIP : %s\n", inet_ntoa(ip->ip_src));
    printf("DstIP : %s\n", inet_ntoa(ip->ip_dst));

jma2000Author Commented:
It compiles but when I run it nothing happens - not sure what that means.
jma2000Author Commented:
sorry..yes it works - now what? :o)
Good question :-)

This means that isn't the format the data is stored.
I think this will restrict your error to the file loading or to the variable where you are loading the file to. I strongly suggest to use unsigned char, since you are dealing with bytes and not chars. Try to allocate pktbuf as

pktbuf = (unsigned char *) malloc(frame_header.bytes_caped + 256);

This is just to make sure that there isn't a memory invasion.  Is the file that you are trying to parse tcpdumped in the same machine you are running this program?
And a last question: is your tcpdump only ip packets? can it be another protocol?

I think we need to check how inet_ntoa() works here. I checked the documentation and saw that inet_ntoa() returns a static string which is overwritten by recursive calls.The length of the returned string is 16 bytes.

It's quite possible that the inet_ntoa() is returning a NON NULL terminated string and printf throws an exception/error dump when it tries to print a NON NULL terminated string.

What you can do is try and copy the contents of innet_ntoa() into a string.

Do this..

initialize a char array of 20 bytes, lets say bye doing..

char op[20];

using.. memset() , set all bytes to NULL.

and then use strcpy(op,inet_ntoa(ip->ip_src));

later put a trace on the character string op[];

and check its contents, you may as well try printing that string.

jma2000Author Commented:
thanks for all the good suggestions..let me try and get back shortly.
jma2000Author Commented:
ok..so far tried the pktbuf = (unsigned char *) malloc(frame_header.bytes_caped + 256); but no luck.  It is a raw binary tcpdump file captured on another system. I read it in ethereal with no problem and I use the tcpdump -x option to convert to HEX from raw binary.  

I will look at the next suggestion now.

Is the another system also a Sun Solaris?
jma2000Author Commented:
I tried the strcopy for strcpy(op,inet_ntoa(ip->ip_src));
 and got a fault on that line.

debug is as follows:  basically all zeros

Program received signal SIGSEGV, Segmentation fault.
0x000113e4 in parse_ip_packet (buf=0x23440 "", total=14) at detect.c:197
197       strcpy(op,inet_ntoa(ip->ip_src));
(gdb) print op
$1 = '\0' <repeats 19 times>
$2 = '\0' <repeats 19 times>
$3 = '\0' <repeats 19 times>
$4 = '\0' <repeats 19 times>
$5 = '\0' <repeats 19 times>
$6 = '\0' <repeats 19 times>
$7 = '\0' <repeats 19 times>
$8 = '\0' <repeats 19 times>
$9 = '\0' <repeats 19 times>
$10 = '\0' <repeats 19 times>
$11 = '\0' <repeats 19 times>
$12 = '\0' <repeats 19 times>
$13 = '\0' <repeats 19 times>
$14 = '\0' <repeats 19 times>
$15 = '\0' <repeats 19 times>
$16 = '\0' <repeats 19 times>
$17 = '\0' <repeats 19 times>
$18 = '\0' <repeats 19 times>
$19 = '\0' <repeats 19 times>
$20 = '\0' <repeats 19 times>
$21 = '\0' <repeats 19 times>
$22 = '\0' <repeats 19 times>
It seams that everything which is not u_char fails. So it might be an alignment problem. You said size_ethernet == 14, so after skipping 14 bytes, the buffer is not dword aligned any more. Can you try the following:

      struct ip           *ip;                  /* The IP header */

      ip = (struct ip*) malloc(sizeof(struct ip));
      memcpy(ip, pktbuf + size_ethernet, sizeof(struct ip));

      printf("SrcIP : %s\n", inet_ntoa(ip->ip_src));
      printf("DstIP : %s\n", inet_ntoa(ip->ip_dst));
jma2000Author Commented:
YES!!! that did it!! I will have to look at this a little to understand but Thanks!!!!!
It seams that the structures must start at an address which is dividable by 4. when you use malloc, this is automatically done:
     pktbuf = malloc(frame_header.bytes_caped + 1);
So the structure pktbuf starts at a 4 byte boundary. But now you assign a new structure inside the existing buffer:
     ip = (struct ip*)(pktbuf + size_ethernet);
which is 14 bytes inside pktbuf. But it should have been 12 or 16 to keep a 4 byte boundary.

Something like this might be possible:
  struct pktbuf  {
      struct eth_header pktbuf_eth;  // asuming sizeof(eth_header)==14
      struct ip pktbuf_ip;
  } __packed;
This way the compiler knows ip does not start at a 4 byte boundary.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Yes cwwkie is correct, that was my initial doubt, when i said that inet_ntoa() returns a 16 byte string.

The C reference manual by Dennis Ritchie Page no 30, point number B.6) Implementation Difference says.

B.6) Structures and strings are aligned on a word boundary in UNIX , not aligned in GCOS


It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.