Forum: PC-Programmierung C - SIGSEGV Segmentation fault


von Daniel A. (daniel-a)


Lesenswert?

Hallo.

Ich Schreibe zurzeit auf Linux einen Webserver basierend auf 
raw-sockets, um diesen später auf Mikrocontrollern verwenden zu können. 
Ich habe bereits das ARP und das ICMP protocol implementiert. Einige 
ping abfragen funktionieren auch, aber dann stürzt das Programm ab. Ich 
finde den fehler einfach nicht, weder mit gdb noch mit valgrind konnte 
ich die Uhrsache finden.

Das Projekt:
https://github.com/Daniel-Abrecht/DPA-UCS/tree/4572546dcaf9bae11d4227ffe5d25165ef1ed3f1

Ausgabe von Valgrind:
1
daniel@daniel-Inspiron-7720:/projects/DPA-UCS$ sudo valgrind ./bin/linux 
2
==4244== Memcheck, a memory error detector
3
==4244== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
4
==4244== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
5
==4244== Command: ./bin/linux
6
==4244== 
7
socket: ok
8
==4244== Syscall param socketcall.bind(my_addr.sa_data) points to uninitialised byte(s)
9
==4244==    at 0x4F3FC07: bind (syscall-template.S:81)
10
==4244==    by 0x400DD6: DPAUCS_ethInit (linux.c:90)
11
==4244==    by 0x400FBC: DPAUCS_init (server.c:29)
12
==4244==    by 0x400A4E: main (main.c:12)
13
==4244==  Address 0xfff0003e4 is on thread 1's stack
14
==4244==  in frame #1, created by DPAUCS_ethInit (linux.c:63)
15
==4244== 
16
wlan0 60:36:dd:26:4a:bc
17
send: 98 98 ok
18
send: 98 98 ok
19
send: 98 98 ok
20
send: 98 98 ok
21
send: 98 98 ok
22
send: 98 98 ok
23
==4244== Invalid read of size 8
24
==4244==    at 0x4028E0: IPv4_transmissionEnd (ip.c:69)
25
==4244==    by 0x40247D: DPAUCS_icmp_handler (icmp.c:18)
26
==4244==    by 0x402DD7: IPv4_handler (ip.c:228)
27
==4244==    by 0x402E49: DPAUCS_ip_handler (ip.c:248)
28
==4244==    by 0x4014DB: DPAUCS_doNextTask (server.c:143)
29
==4244==    by 0x400A76: main (main.c:20)
30
==4244==  Address 0x40 is not stack'd, malloc'd or (recently) free'd
31
==4244== 
32
==4244== 
33
==4244== Process terminating with default action of signal 11 (SIGSEGV)
34
==4244==  Access not within mapped region at address 0x40
35
==4244==    at 0x4028E0: IPv4_transmissionEnd (ip.c:69)
36
==4244==    by 0x40247D: DPAUCS_icmp_handler (icmp.c:18)
37
==4244==    by 0x402DD7: IPv4_handler (ip.c:228)
38
==4244==    by 0x402E49: DPAUCS_ip_handler (ip.c:248)
39
==4244==    by 0x4014DB: DPAUCS_doNextTask (server.c:143)
40
==4244==    by 0x400A76: main (main.c:20)
41
==4244==  If you believe this happened as a result of a stack
42
==4244==  overflow in your program's main thread (unlikely but
43
==4244==  possible), you can try to increase the size of the
44
==4244==  main thread stack using the --main-stacksize= flag.
45
==4244==  The main thread stack size used in this run was 8388608.
46
==4244== 
47
==4244== HEAP SUMMARY:
48
==4244==     in use at exit: 0 bytes in 0 blocks
49
==4244==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
50
==4244== 
51
==4244== All heap blocks were freed -- no leaks are possible
52
==4244== 
53
==4244== For counts of detected and suppressed errors, rerun with: -v
54
==4244== Use --track-origins=yes to see where uninitialised values come from
55
==4244== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

Ausgabe von Ping:
1
daniel@daniel-Inspiron-7720:/datas/projects/DPA-UCS$ ping 192.168.1.29
2
PING 192.168.1.29 (192.168.1.29) 56(84) bytes of data.
3
64 bytes from 192.168.1.29: icmp_seq=1 ttl=64 time=35.1 ms
4
64 bytes from 192.168.1.29: icmp_seq=1 ttl=64 time=35.5 ms (DUP!)
5
64 bytes from 192.168.1.29: icmp_seq=2 ttl=64 time=57.4 ms
6
64 bytes from 192.168.1.29: icmp_seq=2 ttl=64 time=57.9 ms (DUP!)
7
64 bytes from 192.168.1.29: icmp_seq=3 ttl=64 time=80.4 ms
8
64 bytes from 192.168.1.29: icmp_seq=3 ttl=64 time=80.8 ms (DUP!)
9
^C
10
--- 192.168.1.29 ping statistics ---
11
5 packets transmitted, 3 received, +3 duplicates, 40% packet loss, time 4011ms
12
rtt min/avg/max/mdev = 35.107/57.925/80.882/18.514 ms

Verwendeter Compiler:
1
daniel@daniel-Inspiron-7720:~$ gcc --version
2
gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2
3
Copyright (C) 2014 Free Software Foundation, Inc.
4
This is free software; see the source for copying conditions.  There is NO
5
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Ich kann mir einfach nicht erklären, wie die Variable 
currentTransmission.to in der Datei src/server/protocol/ip.c auf Zeile 
69 den Wert 0x40 haben konnte.

Wo hab ich diesmal einen Fehler gemacht? Meine debugging fähigkeiten 
stossen hier an ihre Grenzen.

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

Wo wird denn

1
static stream_t* IPv4_transmissionBegin( void* from, void** to, uint8_t type )

aufgerufen? Bzw. wo werden die drei Parameter, die an die Funktion zu 
übergeben sind, auf ihre richtigen Werte gesetzt? Hab es auf die 
Schnelle durch Ankucken der .c Dateien im Browser nicht gefunden.

Bzw. es könnte hier drin versteckt sein:

1
      while(true){
2
        if(
3
            !(*handler)(
4
              &f->info->src,
5
              &f->info->dest,
6
              &IPv4_transmissionBegin,
7
              &IPv4_transmissionEnd,
8
              f->offset,
9
              f->length,
10
              payload,
11
              !(fragment.flags & IPv4_FLAG_MORE_FRAGMENTS)
12
            )

Aber da mag ich nicht drin rumsuchen. Muss man den Code so verworren 
schreiben, oder geht es auch normal? ;-)
Welchen Übergabewert Parameter an eine Funktion haben bzw. wo genau die 
Zuweisung passiert, sollte in aller Regel auf einen Blick erfassbar 
sein. Wenn das nicht der Fall ist, macht man sich das Leben damit 
unnötig schwer.

Da das Ganze im Moment eh auf Linux läuft, hindert Dich ja nichts daran 
Debug-Informationen auf die Konsole und/oder in eine Datei auszugeben. 
Das wäre so das Erste, was ich persönlich machen würde.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Mark Brandis schrieb:
> Wo wird denn
>
1
> static stream_t* IPv4_transmissionBegin( void* from, void** to, uint8_t 
2
> type )
3
>
>
> aufgerufen? Bzw. wo werden die drei Parameter, die an die Funktion zu
> übergeben sind, auf ihre richtigen Werte gesetzt? Hab es auf die
> Schnelle durch Ankucken der .c Dateien im Browser nicht gefunden.

Das ist in src/server/protocol/icmp.c
1
void* ret[] = { from, 0 };
2
stream_t* stream = (*begin)( to, ret, IP_PROTOCOL_ICMP );
3
DPAUCS_stream_referenceWrite( stream, payload, length );
4
(*end)();

> Bzw. es könnte hier drin versteckt sein:
>
1
>       while(true){
2
>         if(
3
>             !(*handler)(
4
>               &f->info->src,
5
>               &f->info->dest,
6
>               &IPv4_transmissionBegin,
7
>               &IPv4_transmissionEnd,
8
>               f->offset,
9
>               f->length,
10
>               payload,
11
>               !(fragment.flags & IPv4_FLAG_MORE_FRAGMENTS)
12
>             )
13
>

Ist es irgendwie auch. Handler ist auf DPAUCS_icmp_handler gesetzt. 
Kompt von src/main.c:
1
DPAUCS_addIpProtocolHandler(IP_PROTOCOL_ICMP,&DPAUCS_icmp_handler);
Villeicht sollte ich das in DPAUCS_icmpInit() umbenenenn...

> Aber da mag ich nicht drin rumsuchen. Muss man den Code so verworren
> schreiben, oder geht es auch normal? ;-)

Nunja, später soll handler auch &DPAUCS_tcp_handler oder 
&DPAUCS_udp_handler sein können und ich will mir die möglichkeit 
offenhalten später auch z.B. IPv6 zu implementieren, ohne später höhere 
layer verändern zu müssen. Ausserdem will ich icmp oder tcp support 
einfach deaktivieren können, ohne viel am Code ändern zu müssen.
Was wäre den hier gut und normal? Wäre ein switch case und week symbols 
besser? Oder ein switch case mit Makros?

> Welchen Übergabewert Parameter an eine Funktion haben bzw. wo genau die
> Zuweisung passiert, sollte in aller Regel auf einen Blick erfassbar
> sein. Wenn das nicht der Fall ist, macht man sich das Leben damit
> unnötig schwer.

Ist dass den nicht immer schwierig wenn man ein Projekt zum ersten mal 
sieht?

> Da das Ganze im Moment eh auf Linux läuft, hindert Dich ja nichts daran
> Debug-Informationen auf die Konsole und/oder in eine Datei auszugeben.
> Das wäre so das Erste, was ich persönlich machen würde.

Stimmt, das werde ich morgen mal versuchen.

von Daniel A. (daniel-a)


Lesenswert?

Debug ausgaben auf der Konsole auszugeben hat mir nur Dinge gezeigt, die 
ich sowiso schon wusste. Mit gdb konnte ich dass problem jetzt jedoch 
eindeutig identifizieren. Es ist ein Buffer Overflow.

Ausschnitt aus gdb:
1
Hardware watchpoint 2: currentTransmission.from
2
3
Old value = (void *) 0x7fffffffe3bc
4
New value = (void *) 0x1
5
0x000000000040189d in DPAUCS_stream_referenceWrite (stream=0x6a7120 <outputStream>, 
6
    p=0x6a7264 <packetInputBuffer+36>, size=64) at src/server/stream.c:32
7
32    BUFFER_PUT(stream->buffer_buffer,entry);
8
(gdb) backtrace
9
#0  0x000000000040189d in DPAUCS_stream_referenceWrite (stream=0x6a7120 <outputStream>, 
10
    p=0x6a7264 <packetInputBuffer+36>, size=64) at src/server/stream.c:32
11
#1  0x0000000000402473 in DPAUCS_icmp_handler (from=0x7fffffffe3b0, to=0x7fffffffe3bc, 
12
    begin=0x402552 <IPv4_transmissionBegin>, end=0x402738 <IPv4_transmissionEnd>, offset=0, length=64, 
13
    payload=0x6a7264 <packetInputBuffer+36>, last=true) at src/server/protocol/icmp.c:17
14
#2  0x0000000000403102 in IPv4_handler (info=0x7fffffffe430, ip=0x6a7250 <packetInputBuffer+16>)
15
    at src/server/protocol/ip.c:279
16
#3  0x0000000000403174 in DPAUCS_ip_handler (info=0x7fffffffe430) at src/server/protocol/ip.c:299
17
#4  0x00000000004014dc in DPAUCS_doNextTask () at src/server/server.c:143
18
#5  0x0000000000400a77 in main () at src/main.c:20
19
(gdb) print *stream->buffer_buffer
20
$3 = {buffer = 0x6a8100 <outputStreamBufferBuffer_buffer>, size_mask = 63, read_offset = 6, write_offset = 6}
21
(gdb) print (stream->buffer_buffer)->write_offset & (stream->buffer_buffer)->size_mask 
22
$5 = 6
23
(gdb) print sizeof(outputStreamBufferBuffer_buffer)
24
$6 = 144
25
(gdb) print sizeof(outputStreamBufferBuffer_buffer) / sizeof(*outputStreamBufferBuffer_buffer)
26
$7 = 6

BUFFER_PUT:
1
#define BUFFER_PUT(buf,x) \
2
  do { \
3
    (buf)->buffer[ (buf)->write_offset & (buf)->size_mask ] = x; \
4
    (buf)->write_offset++; \
5
  } while(0)

Der Fehler war hier:
1
#define DEFINE_BUFFER(B,T,name,size) \
2
  static B name ## _buffer[size]; \
3
  T name = { name ## _buffer, (1<<size)-1, 0, 0 }

Korrekt wäre gewesen:
1
#define DEFINE_BUFFER(B,T,name,size) \
2
  static B name ## _buffer[1<<size]; \
3
  T name = { name ## _buffer, (1<<size)-1, 0, 0 }

von Mark B. (markbrandis)


Lesenswert?

Daniel A. schrieb:
> Das ist in src/server/protocol/icmp.c

Bei einer static-Funktion fände ich es sinnvoller, wenn man dies auch im 
gleichen Modul wiederfinden würde.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Ich verstehe das static Keyword bei globalen Symbolen als prinzip der 
Sichtbarkei derselben, welches dazu benutzt werden kann die direkte 
verwendung des Symbols in anderen Compilationsunits zu verhindern. Wo 
die Variable verwendet wird spielt keine Rolle, wichtig ist, dass das 
Symbol nur in der selben Compilationsunit verwendet werden kann und 
damit die versehentliche Mehrfachdefinition von Symbolen mit 
unterschiedlichem zweck vermieden wird. Ich weiss, das es in diesem fall 
nicht notwendig wäre, aber ich versuche meine Designgrundsätze 
konsistent beizubehalten. Ausserdem kommt man so nicht auf die Idee nach 
dem Symbol an Orten zu suchen, wo es nicht vorkommen kann.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.