3

I have the following struct:

typedef struct ann_t {
  uint32_t xs;
  uint32_t hs;
  uint32_t ys;
  float *x;
  float *h;
  float *y;
  float **wxh;
  float **why;
} ann_t;

Initialized in the following way:

ann_t* ann_init(uint32_t xs, uint32_t hs, uint32_t ys) {
  ann_t *ann = malloc(sizeof(ann_t));
  ann->xs = xs;
  ann->hs = hs;
  ann->ys = ys;
  ann->x = calloc(xs, sizeof(float));
  ann->h = calloc(hs, sizeof(float));
  ann->y = calloc(ys, sizeof(float));

  
  ann->wxh = calloc(xs, sizeof(float*));
  ann->why = calloc(hs+1, sizeof(float*));

  
  int i, j;
  for(i = 0; i < xs; i++) {
    ann->wxh[i] = malloc(hs * sizeof(float));
  }

  for(i = 0; i < hs+1; i++) {
    ann->why[i] = malloc(ys * sizeof(float));
  }
  // printf("%p\n", ann->x);
  return ann;
}

Including this code in another program:

  ...

  ann_t *ann = ann_init(25, 10, 4);
  // printf("%p\n", ann->x);
  ann->x[0] = 1.0;

  ...

The result is:

Segmentation fault (core dumped)

Using valgrind:

==26436== Use of uninitialised value of size 8

...

==26436==

==26436== Invalid write of size 4

...

==26436== Address 0x4c3a78000000000 is not stack'd, malloc'd or (recently) free'd

I tried to reproduce this in a smaller program but couldn't.

Changing the struct to have uint64_t instead of uint32_t solves the problem.

Printing the pointer ann->x inside ann_init I get 0x55601051f080 and outside 0x1051f08000000000.

Why does this happen?

EDIT: Found the culprit in one of the included files:

#pragma pack(1)

Still not sure why this causes the problem.

If I was doing pointer arithmetic to access the struct fields this would make sense but I'm accessing the struct field by name so why does it calculate the wrong value?

Why is it fine inside the init function but outside it the access fails?

Community
  • 1
  • 1
Milton Silva
  • 133
  • 4
  • 4
    Please show all the code needed to reproduce the problem. Please post a [MCVE]. Most probably, the problem is somewhere else. – KamilCuk Jan 09 '20 at 13:36
  • 1
    Build with debugging information enabled (add the `-g` option when building) and Valgrind should be able to give you file-names and line-numbers where the problems are detected in your code. Also use a debugger to catch the crash in action, and locate where in your code it happens, and examine the values of all involved variables at that point. – Some programmer dude Jan 09 '20 at 13:37
  • 1
    I just ran your code, just as you have it, and it ran without a problem. So I suspect that something you are not showing may be contributing to the seg fault, or perhaps there is undefined behavior lurking somewhere. I agree with KamilCuk's suggestion: edit your post to include _the relevant area of code_. – ryyker Jan 09 '20 at 13:41
  • The code crashes at ```ann->x[0] = 1.0;```. There are no lines between the init and the assignment. Regarding the minimal example, I tried to make a minimal example but can't reproduce the problem except when I include it in this program. Besides posting the full program. Any other info I can provide? – Milton Silva Jan 09 '20 at 13:48
  • 3
    This smells like the structure is declared differently for both program parts... Either you changed the struct definition, but did not recompile one source file, but the other; or you use two different struct definitions, i.e. in different header files. Begin with deleting all object files and then recompile – Ctx Jan 09 '20 at 13:52
  • 1
    General comment: you have not validated that calloc and malloc were successful. Highly unlikely that they'll fail given your current input, but is still good practice. – Tomer Jan 09 '20 at 14:00
  • Thank you for the comments. Due to the insistence on posting a minimal example I managed to track it down to a particular file included that had the following: `#pragma pack(1)`. That was the cause :( – Milton Silva Jan 09 '20 at 14:15
  • Thanks for the update, @MiltonSilva. Glad you got your problem fixed. Here is what looks like a useful link for future visitors: https://stackoverflow.com/questions/3318410/pragma-pack-effect – Edd Inglis Jan 09 '20 at 14:23
  • 3
    "Still not sure why this causes the problem." It is _likely_ (but not certain without seeing your code) that one file has the `#pragma' before the struct definition (or its inclusion from a header) and one file does not. Thus both files think they're looking at the same structure, but one expects padding and one does not. Hence when you come to de-reference the pointers, they're in the wrong place and all hell breaks loose. Actually, you can sort of see this in your printf example: the bottom of one address matches the top of the other; that's super unlikely by chance. – Edd Inglis Jan 09 '20 at 14:28
  • @EddInglis yes, that is it. Can you post the answer for me to mark it as answered? – Milton Silva Jan 09 '20 at 14:34

1 Answers1

3

An answer derived from the comments to the question...

Assume you have a simpler structure defined in a header:

// header.h
typedef struct {
  char foo;
  int *ptr;
} fish_t;

and two source files:

// src1.c
#include "header.h"

int dummy_int = 5;

fish_t my_fish;
my_fish.foo = 'a';
my_fish.ptr = &dummy_int;

use_fish_fn( &my_fish );
// src2.c
#pragma pack(1)
#include "header.h"

void use_fish_fn( fish_t *f )
{
  int bar = *f->ptr;
}

The first file (without packing) likely sees fish_t as having a memory layout like:

0: |  foo  |  pad  |  pad  |  pad  |     // one byte char, three bytes padding
4: |              ptr              |     // four byte pointer

but the second file (with packing) see it like:

0: |  foo  |        ptr ...        |     // one byte char, 3/4 of pointer
4: |...ptr |                             // last part of pointer

So when the second file tries to read (and subsequently dereference) the pointer it's actually reading whatever happened to be in the padding, and that's definitely going to go wrong.

Edd Inglis
  • 1,067
  • 10
  • 22