Why is this pointer-fu segfault?

I seem to have fallen within the bounds of my Pointer-Fu and am seeking help (or some kind of head medicine).

Rough outline of the project: an integrated ARM video encoder board running Linux using a poorly documented poorly supported SDK. Among his extensive code sprawl is the huge heap that gSoap generates from some WSDL, and this causes a headache.

In terms of the huge data structure automatically created by gSoap, we have a place to write some data (or a place to write a pointer to the place where we wrote some data):

struct tt__IPAddress { enum tt__IPType Type; /* required element of type tt:IPType */ char *IPv4Address; /* optional element of type tt:IPv4Address */ char *IPv6Address; /* optional element of type tt:IPv6Address */ }; 

Then we have this code, which, in short, should write a string to the IPv4Address address:

 DNSInformation->DNSManual = ((struct tt__IPAddress *)soap_malloc(soap, sizeof(struct tt__IPAddress))); DNSInformation->DNSManual->IPv4Address = (char **)soap_malloc(soap, sizeof(char *)); DNSInformation->DNSManual->IPv4Address[0] = (char *)soap_malloc(soap, sizeof(char) * LARGE_INFO_LENGTH); // Code crashes at this next line: strncpy(*DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH-1); 

dns_string is what you expect - something like "192.168.2.254". It correctly completes zero, the value of LARGE_INFO_LENGTH is something big (for example, 1024), so it has enough space for the string. I switched from strcpy () to strncpy () for security.

My background is a little embedded material (without OS, without using malloc ()), so I have a bit of persuasion problem. I understand what this code does. The code is automatically generated / part of the SDK, so this is not my creation, and it is not documented / commented out.

Here is what I think he is doing:

 DNSInformation->DNSManual = ((struct tt__IPAddress *)soap_malloc(soap, sizeof(struct tt__IPAddress))); 

Allocates a piece of RAM pointed to by DNSManual where the tt__IPAddress structure will exist.

 DNSInformation->DNSManual->IPv4Address = (char **)soap_malloc(soap, sizeof(char *)); 

Allocates a piece of RAM pointed to by IPv4Address, where a pointer to a string containing the address will be written.

 DNSInformation->DNSManual->IPv4Address[0] = (char *)soap_malloc(soap, sizeof(char) * LARGE_INFO_LENGTH); 

Now it throws me a little, it looks like he is trying to allocate RAM to store the string that IPv4Address [0] will point to, except that it seems to me that they are trying to write a (32-bit) pointer to char, perhaps.

This code worked previously, however after some changes elsewhere it is now segfaults, always on or during strncpy ().

My questions are twofold:

  • Can someone help me understand what is happening with mallocs / pointer-fu correctly?
  • Any recommendations on how to track / debug this?

We do not have the ability to GDB with this setting, unfortunately - yes, I'm sure it is possible to configure, but for now let's assume that it is not practical for many lame and tiring reasons.

Currently, I have printf debugging scattered throughout the code, on virtually every line of this small fragment, and it always stops with SIGSEGV in the strncpy () line.


Edit to close how WhozCraig hit the answer:

For the most well-known reasons, gSoap changed the structure of tt__IPAddress, it may have run out of asterisks, but what it was in previous versions, and what it should be is:

 struct tt__IPAddress { enum tt__IPType Type; char **IPv4Address; /* note ptr to ptr */ char **IPv6Address; }; 
+4
source share
4 answers

The code does not match the structure structure. Layout:

  struct tt__IPAddress { enum tt__IPType Type; /* required element of type tt:IPType */ char *IPv4Address; /* optional element of type tt:IPv4Address */ char *IPv6Address; /* optional element of type tt:IPv6Address */ }; 

Value: IPv4Address is a char pointer. but this:

 DNSInformation->DNSManual->IPv4Address = (char **)soap_malloc(soap, sizeof(char *)); 

assigns it char ** . but the type is still char * , so this is:

 strncpy(*DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH-1); 

dereferences the specified pointer to a single char , which I can assure that you are NOT compatible with char * on your platform (and probably any other, for that matter).

This compilation should have at least warnings and direct errors, if your compiler has any brains at all. It looks like it was original to be as follows:

  struct tt__IPAddress { enum tt__IPType Type; char **IPv4Address; /* note ptr to ptr */ char **IPv6Address; }; 

for a dynamic array of pointers, each pointer is a dynamically allocated memory for one IP address. if that were so, it would make much more sense. However, if you plan to use only one IPv4 address for each structure, then you need to change this:

 DNSInformation->DNSManual = soap_malloc(soap, sizeof(struct tt__IPAddress))); if (DNSInformation->DNSManual) { DNSInformation->DNSManual->IPv4Address = soap_malloc(soap, sizeof(char) * LARGE_INFO_LENGTH); if (DNSInformation->DNSManual->IPv4Address) { strncpy(DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH-1); DNSInformation->DNSManual->IPv4Address[LARGE_INFO_LENGTH-1] = 0; } } 

Or something like that.

+2
source

I think it looks broken.

It:

 char *IPv4Address; /* optional element of type tt:IPv4Address */ 

says that IPv4Address is the only pointer to character data, i.e. line.

But then it was used as follows:

 DNSInformation->DNSManual->IPv4Address = (char **)soap_malloc(soap, sizeof(char *)); 

This is simply wrong. Assuming that the correct return value for soap_malloc() (i.e. void * matches malloc() ), no cast is required, but the fact that casting differs from the actual signal of the type is some error.

It treats the IPv4Address structure IPv4Address as a pointer to a pointer that is clearly not.

0
source

I'm sure it should look something like this:

 DNSInformation->DNSManual = soap_malloc(soap, sizeof(struct tt__IPAddress))); DNSInformation->DNSManual->IPv4Address = soap_malloc(soap, sizeof(char) * LARGE_INFO_LENGTH); strncpy(DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH-1); 

Your structure contains pointers to strings, but first it allocates an array of pointers (char **) and then allocates memory for the first pointer in this array.

and don't forget to set binary zero after you used strncpy () as it does not set it itself.

// Edit: the first part was wrong, sorry

0
source

Here is a working solution (I used malloc instead of soap_malloc , etc.):

 #include <stdio.h> #include <stdlib.h> #define LARGE_INFO_LENGTH 1024 enum tt__IPType { tt__IPv4, tt__IPv6 }; struct tt__IPAddress { enum tt__IPType Type; /* required element of type tt:IPType */ char *IPv4Address; /* optional element of type tt:IPv4Address */ char *IPv6Address; /* optional element of type tt:IPv6Address */ }; struct tt__DNSInformation { struct tt__IPAddress* DNSManual; }; int main() { struct tt__DNSInformation* DNSInformation; char dns_string[] = "192.168.2.254"; DNSInformation = malloc(sizeof(struct tt__DNSInformation)); DNSInformation->DNSManual = malloc(sizeof(struct tt__IPAddress)); DNSInformation->DNSManual->IPv4Address = malloc(sizeof(char) * LARGE_INFO_LENGTH); strncpy(DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH - 1); printf("%s\n", DNSInformation->DNSManual->IPv4Address); return 0; } 
0
source

Source: https://habr.com/ru/post/1499195/


All Articles