solaris에서 한구조체 안에서 멤버1(구조체)이 다른 멤버 참조할

zingle의 이미지


/* 구조체 선언 */
struct st1 {
.....
struct ip *iph;

.....
u_char *buf;
....
}


int main(.....) {
  ....
  struct st1 packet;
  ....
  /* packet.buf에 raw패킷 데이터 입력 */
  ....
  packet.iph = packet.buf + <어쩌구어쩌구값>;
  ....
  handleIP(&packet);
   ....
}


int handleIP(struct st1 *packet) {
  ...
  struct ip *iph = packet->iph;
  ...

  fprintf(stderr, "%d", iph->ip_hl);

  ...

}

리눅스에서 짠 소스를 solaris로 포팅 작업 중입니다.
하나의 큰 구조체 안에서 raw 데이터를 가지고 있고,
다른 멤버(구조체)에서 그 데이터를 참조하는 구조를 가지고 있습니다.

리눅스에서는 잘 돌아갔습니다.
그런데 solaris에서는 이렇게 쓸수가 없더군요.
위의 코드에서처럼 참조하는 구조체의 멤버에 접근하면 그 순간 죽습니다.

일단은 포인터를 쓰지 않고 그때 그때 다른 버퍼에 복사해서 쓰도록 하고 있습니다만
왜 저런 현상이 나타나는지 모르겠습니다. 컴파일러는 gcc 2.95.3을 사용중이고 OS는 Solaris 8입니다.

alignment 문제일지도 모른다는 생각이 들지만,
확인해보기가 어렵네요. ^^;;

조언 부탁드립니다.

sharefeel의 이미지

프로그램이 죽는다고 하셨는데..
버스 에러가 나지 않는지요?

버스 에러라면 alignment가 안맞아서 나는 것 맞습니다.
캐스팅 하다보면 alignment가 맞지 않게 메모리 access가 일어나게 되는데..
곧바로 에러 납니다..

인텔 머신에서는 두워드에 걸쳐 있어도..
CPU가 알아서 읽어줍니다만..
RISC 머신에서는 버스에러가 난다고 합니다..

===============
Vas Rel Por

osanha의 이미지

테스트는 해보지 않아서 확실한건 아니지만 포인터 사용에 좀 문제가 있어 보이는군요.

struct ip* iph 에서 iph는 struct ip 를 가리키는 포인터입니다.
ip의 구조를 어떻게 정의해놓으신건지는 모르겠지만..

packet.iph = packet.buf + <어쩌구어쩌구값>;
buf 는 char 를 가리키는 포인터입니다..

물론, 둘다 32비트 주소값을 가지고 있는 같은 크기를 가진 포인터이지만 분명히 가리키는 형이 다릅니다.

포인터 형에 따라 포인터값에 산술연산을 했을때 증감하는 주소 크기가 다르지요..아시겠지만.

struct ip *iph = packet->iph;
여기서 사용은 정확하게 되었습니다만, iph가 위에서 char* 를 대입받았으므로 문제가 발생할 소지가 생기게 됩니다. 아니, char* 을 받는것 자체가 문제라고 할 수 있겠네요.

제 생각엔, 위에 저 부분을...
char* temp = packet.buf + <어쩌구저쩌구값>;
packet.iph = (struct ip*) temp;
이렇게 형변환을 명확하게 하신 후에... 밑에서 에러가 나는지 한번 확인해보는게 어떨련지..

혹은 이렇게 바로 해버려도 될듯합니다.

packet.iph = (struct ip*) (packet.buf + <어쩌구저쩌구값>);

저렇게 해서 해결이 될련진 확실히 말할순없지만,, 어쨋든. OS나, 컴파일러에 따라서 조금 불완전한 코드도 원하는대로의 동작을 하게 컴파일될수도 있지만, 가능하면 코드자체가 완벽하고 명확해야 한다고 봅니다..또한 미리부터 호환성을 염두에두고 개발하는것도 필요하겠지요.

zingle의 이미지

원래코드를 옮긴게 아니라 그 흐름만을 옮기다 보니
실수가 있었습니다. ^^;

  packet.iph = packet.buf + <어쩌구어쩌구값>; 

부분은 사실은

  packet.pptr = packet.buf + <어쩌구 저쩌구값>;
  packet.iph = (struct ip*)packet.pptr;

이렇게 구성되어 있습니다. 멤버를 다 적기가
머해서 단순하게 보여 드리려고 하다가 캐스팅을 빼먹는 실수를 했네요. ^^

버퍼안에는 당연히 struct ip의 데이터가 들어있습니다.
<어쩌구 저쩌구값>은 그 앞에 다른 헤더 값이구요. ^^

osanha의 이미지

음...

그리 긴게 아니라면 좀더 소스를 상세하게 올려주시면, 혹은 다 올려주시면..좀더 도움이 될듯합니다..어쩌구저쩌구도 좀 보고싶고 buf에 어떤값을 넣는지도...

zingle의 이미지

소스가 좀 많이 길어서 올리기가 머하군요. ^^; 죄송합니다.

zingle의 이미지

....

zingle의 이미지

디버그 중에 알아낸 사실 등으로 현재 상태를 좀더 정확하게 알려드리겠습니다. ^^ 그냥 소스를 올려드리면 편할텐데 회사 솔루션인지라..^^;

/* /usr/include/netinet/ip.h에 정의된 struct ip입니다. */
struct ip {
#ifdef _BIT_FIELDS_LTOH
        uchar_t ip_hl:4,                /* header length */
                ip_v:4;                 /* version */
#else
        uchar_t ip_v:4,                 /* version */
                ip_hl:4;                /* header length */
#endif
        uchar_t ip_tos;                 /* type of service */
        short   ip_len;                 /* total length */
        ushort_t ip_id;                 /* identification */
        short   ip_off;                 /* fragment offset field */
#define IP_DF 0x4000                    /* dont fragment flag */
#define IP_MF 0x2000                    /* more fragments flag */
        uchar_t ip_ttl;                 /* time to live */
        uchar_t ip_p;                   /* protocol */
        ushort_t ip_sum;                /* checksum */
        struct  in_addr ip_src, ip_dst; /* source and dest address */
};

위에 예시 소스에서 틀린 부분하고 몇 가지 추가한 소스입니다.

/* 구조체 선언 */ 
struct st1 { 
..... 
struct ip *iph; 

..... 
u_char *buf; 
u_char *pptr;
.... 
} 


int main(.....) { 
  .... 
  struct st1 packet; 
  .... 
  /* packet.buf에 raw패킷 데이터 입력 */ 
  .... 
  packet.pptr = packet.buf + <어쩌구어쩌구값>; 
  packet.iph   = packet.pptr;
  ...
  /* 아래와 같이 특정 멤버에 접근하는 경우에 죽어버립니다. */
  fprintf(stderr, "%d", iph->ip_hl);  /* 정상적으로 출력됩니다. */
  fprintf(stderr, "%d", iph->ip_v);  /* 죽습니다. --V */
  .... 
  handleIP(&packet); 
   .... 
} 


int handleIP(struct st1 *packet) { 
  ... 
  struct ip *iph = packet->iph; 
  ... 

  fprintf(stderr, "%d", iph->ip_hl);  /* 역시 정상적으로 출력됩니다. */
  fprintf(stderr, "%d", iph->ip_v);  /* 역시 죽습니다. --V */

  ... 

} 

위에 주석에 적은 것 처럼
구조체 안에 버퍼(buf)가 있고,
그 버퍼에서 현재 분석중인 곳을 가리가키는 포인터(pptr)가 있습니다.
(struct ip *iph)는 현재 pptr이 가리키는 곳에 lay됩니다.

iph에서 ip_hl, ip_tos 등을 출력했을 때 문제 없이 출력되지만
특정한 값들(ip_v, ip_src, ip_dst 등)을 출력하려고 하면
프로그램이 죽습니다.

출력이 되는 다른 값들이 정확한 걸로 보아서 pptr이 엉뚱한 곳을 가리키고 있거나 하지는 않은 것으로 보입니다.

플랫폼은 solaris 8이고, gcc는 2.95.3을 사용 중입니다.

^^

cdpark의 이미지

alignment 문제입니다. <어쩌구저쩌구> 값이 아마도 홀수거나 2의 배수겠죠? 4의 배수가 아닌 이상 SPARC CPU에서는 위험한(!) 코드입니다.

그리고 4bit짜리 bit filed 둘이 한 byte에 들어간다는 보장도 없습니다.

zingle의 이미지

네, 어쩌구 저쩌구 값은 보통 14바이트(ethernet header length)가 됩니다.
그런데 해당 모듈을 컴파일 하면서 -fpack_struct 옵션을 줬는데도
마찬가지 현상이 발생합니다.
단순히 저 옵션으로는 해결을 할 수 없는 건가요?

ip 헤더라서 그 헤더를 수정할 수 없는 상황이어서요.^^

alwaysrainy의 이미지

-Wall 옵션으로 컴파일 시에 아무 에러 없었나요? 물론.. 아니겠지만..

Quote:
일단은 포인터를 쓰지 않고 그때 그때 다른 버퍼에 복사해서 쓰도록 하고 있습니다만.

혹시라도!! 실수로 packet.buf가 main 함수내의 자동 변수를 가리키게 하신건
아닌가요?

---------------------------------------
세계는 넓고, 할일은 많다.

zingle의 이미지

-Wall해서 컴파일합니다만,
아무런 에러도 없었습니다. ^^;;

packet.buf는 xcalloc을 통해서 동적으로 할당했습니다.

^^

zingle의 이미지

#include <net/if.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <arpa/telnet.h>
#include <resolv.h>

#include <netinet/if_ether.h>
#include <netinet/in_systm.h>

#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

typedef struct _ST1 {
        struct ip *iph; /* ip header */
        u_char *buf;            /* packet buffer */
        u_char *ptr;            /* position in buffer */
} ST1;

char data[] = {  /* A random packet. */
"\x00\x30\x05\x30\x7b\x62\x00\x50\xfc\x8b\x12\xd2\x08\x00\x45\x00"
"\x00\x28\x14\x0a\x40\x00\x80\x06\xbc\xe8\x0a\x0a\x0a\xf2\x0a\x0a"
"\x0a\xd8\x0e\xd9\x01\xbb\xc4\xf2\x62\xd2\x96\xe5\xce\x1a\x50\x10"
"\xfa\xf0\xed\xac\x00\x00\x20\x20\x20\x20\x20\x20"
};

int main (int argc, char *argv[])
{

        ST1 tmpST1;
        struct ip *iph;

        /* allocate memory */
        tmpST1.buf = (u_char*)malloc(sizeof(char)*1500);
        if (tmpST1.buf == NULL) {
                fprintf(stderr, "malloc error!\n");
                return -1;
        }

        /* put data into the buffer */
        memset(tmpST1.buf, '\0', sizeof(char)*1500);
        memcpy(tmpST1.buf, (u_char*)data, sizeof(data));

        tmpST1.ptr = tmpST1.buf + 14; /* move pointer to iphdr */

        iph = (struct ip*)tmpST1.ptr;

        fprintf(stderr, "[sizeof(ip)][%d]\n", sizeof(struct ip));
        fprintf(stderr, "[sizeof(inaddr)][%d]\n", sizeof(struct in_addr));
        fprintf(stderr, "[buf       ][%p]\n", tmpST1.buf);
        fprintf(stderr, "[ptr       ][%p]\n", tmpST1.ptr);
        fprintf(stderr, "[ip_hl    ][%d]", iph->ip_hl);  //<---mis alignment!!!


        /* close */
        tmpST1.ptr = NULL;
        iph = NULL;

        free (tmpST1.buf);
        return 1;

}

*SUN Fire280R
*GCC 2.95.3

소스를 단순화 시킨 예제코드를 돌려보니 BUS Error가 났습니다.
따라서 alignment 문제인 것으로 확신이 들어서
관련 자료들을 검색해봤는데,
해결 방법으로는 별로 뾰족한 수가 없더군요.

저같은 경우에는 구조체 멤버를 변경하는 것이 불가능해서
padding char같은걸 추가하는 것은 해결방법이 되지 못했습니다.

한군데 찾은 것은 snort-dev mailing list에서 그나마 좀 비슷한 문제에 대한 해결책이 있더군요.
https://citadelle.intrinsec.com/mailing/current/HTML/ml_snort-dev/1888.html

일단은 버퍼에 lay-over하는 구조체를 정의하면서 "__attribute__((packed))"라는 문구를 삽입하는 것인데, 주요 tcp/ip 헤더 구조체에 적용해서 돌려보니 일단은 정상적으로 돌아가네요. 물론 실망에서는 어떻게 될지 조금더 봐야하고, 또 해당 쓰레드의 마지막에 가시면, 이걸로 완벽하게 해결된 것은 아닌듯하여 고민이기두 하구요.

혹시 이와 관련한 비슷해서 비슷한 문제가 있으신 분들 참고하시라고 정리해서 올립니다. 그리고 혹시 이와 관련해서 다른 해결책이 있는 분들 계시면 조언 부탁드릴께요~ ^^

bh의 이미지

osanha wrote:
가능하면 코드자체가 완벽하고 명확해야 한다고 봅니다..또한 미리부터 호환성을 염두에두고 개발하는것도 필요하겠지요.

공감이 가네요,,

--
이 아이디는 이제 쓰이지 않습니다.

zingle의 이미지

Quote:
osanha 씀:
가능하면 코드자체가 완벽하고 명확해야 한다고 봅니다..또한 미리부터 호환성을 염두에두고 개발하는것도 필요하겠지요.

공감이 가네요,,

괜히 찔려서 한마디 붙입니다. :wink:

packet을 decode할때에 만약 해당 데이터를 바이트 단위로 copy해서 쓴다면
alignment가 문제가 되지는 않을 것입니다.

하지만 초당패킷수가 많은 경우에는 카피하는 것 자체도 부하가 되기에 카피하지 않는 방법을 찾다보니 이런 방식을 사용하게되었습니다.

이번에 보니 snort와 같은 프로그램에서도 패킷 버퍼의 주소를 argument로 받아서 구조체를 그 위에 얹어서 decode하더군요.
(잠깐 본거라 다를지도 모릅니다. 비슷한 방식으로 디코드하는데 문제가 없는 걸 보면..--;;; :? )

최종호의 이미지

__attribute__ ((packed)) 를 사용하면 struct 정의시에 padding을 사용안하는 것 뿐만이 아니라 메모리 접근시에도 이를 염두에 두어서 처리를 하네요?

아래 소스코드랑 어셈코드를 보시면 확인하실 수 있을 겁니다.

테스트환경:
% uname -a
SunOS hostname 5.7 Generic_106541-23 sun4u sparc SUNW,Ultra-80

% gcc --version
3.0.3

#include <stdio.h>

typedef struct {
        char    a[1];
        int     b;
        char    c[2];
} __attribute__ ((packed)) packed_struct;

typedef struct {
        char    a[1];
        int     b;
        char    c[2];
} unpacked_struct;


main()
{
        packed_struct   packed;
        unpacked_struct unpacked;

        memset(&packed, 0, sizeof(packed));
        memset(&unpacked, 0, sizeof(unpacked));

        printf("sizeof(packed) = %d\n", sizeof(packed));
        printf("&packed.a = %x\n", &packed.a);
        printf("&packed.b = %x\n", &packed.b);
        printf("&packed.c = %x\n", &packed.c);

        printf("sizeof(unpacked) = %d\n", sizeof(unpacked));
        printf("&unpacked.a = %x\n", &unpacked.a);
        printf("&unpacked.b = %x\n", &unpacked.b);
        printf("&unpacked.c = %x\n", &unpacked.c);


        printf("packed op: %d\n", 1 + packed.b);
        printf("unpacked op: %d\n", 1 + unpacked.b);
}

수행결과:

sizeof(packed) = 7
&packed.a = ffbef848
&packed.b = ffbef849
&packed.c = ffbef84d
sizeof(unpacked) = 12
&unpacked.a = ffbef838
&unpacked.b = ffbef83c
&unpacked.c = ffbef840
packed op: 1
unpacked op: 1

어셈블리코드 일부(1 + struct.b 를 printf하는 부분):

...
        sethi   %hi(16776192), %o0
        or      %o0, 1023, %o1
        ld      [%fp-24], %o0
        and     %o0, %o1, %o0
        sll     %o0, 8, %o1
        ld      [%fp-20], %o0
        srl     %o0, 24, %o0
        or      %o0, %o1, %o0
        add     %o0, 1, %o1
        sethi   %hi(.LLC8), %o0
        or      %o0, %lo(.LLC8), %o0
        call    printf, 0
         nop
        ld      [%fp-36], %o0
        add     %o0, 1, %o1
        sethi   %hi(.LLC9), %o0
        or      %o0, %lo(.LLC9), %o0
        call    printf, 0
         nop
...

어셈코드를 보면 1 + packed.b 를 계산하기 위해서
packed.b 의 3바이트를 포함하고있는 워드를 읽어서 왼쪽으로 8bit 쉬프트하고,
packed.b의 나머지 1바이트를 포함하고 있는 워드를 읽어서 오른쪽으로 4bit 쉬프트해서
그 둘을 OR(더해서) 시켜서 그 결과에 1을 더하는 방법으로 수행하네요.

packed 변수가 미리 컴파일시에 스택상의 위치가 알려져서 문제가 없을 수도 있지 않을까 하고 생각을 해서,
이번에는 packed->b 의 위치가 런타임시에 달라져서 컴파일시에 미리 그 위치가 워드의 어느 경계에 있을지 모르도록 프로그램을 수정해 봤습니다.

#include <stdio.h>

typedef struct {
        char    a[1];
        int     b;
        char    c[2];
} __attribute__ ((packed)) packed_struct;

main()
{
        packed_struct   *packed;
        char    buf[1024];
        int     i;

        memset(&packed, 0, sizeof(packed));
        memset(buf, 0, 1024);

        for (i = 0; i < 4; i++) {
                packed = (packed_struct *)(buf + i);
                printf("result is %d\n", 1 + packed->b);
        }
}

수행결과:

result is 1
result is 1
result is 1
result is 1

어셈블리코드:

        .file   "malaligned2.c"
        .section        ".rodata"
        .align 8
.LLC0:
        .asciz  "result is %d\n"
        .section        ".text"
        .align 4
        .global main
        .type   main,#function
        .proc   04
main:
        !#PROLOGUE# 0
        save    %sp, -1152, %sp
        !#PROLOGUE# 1
        add     %fp, -20, %o0
        mov     0, %o1
        mov     4, %o2
        call    memset, 0
         nop
        add     %fp, -1048, %o0
        mov     0, %o1
        mov     1024, %o2
        call    memset, 0
         nop
        st      %g0, [%fp-1052]
.LL2:
        ld      [%fp-1052], %o0
        cmp     %o0, 3
        ble     .LL5
        nop
        b       .LL3
         nop
.LL5:
        add     %fp, -1048, %o1
        ld      [%fp-1052], %o0
        add     %o1, %o0, %o0
        st      %o0, [%fp-20]
        ld      [%fp-20], %o1
        ldub    [%o1+1], %o0
        and     %o0, 0xff, %o0
        sll     %o0, 24, %o2
        ldub    [%o1+2], %o0
        and     %o0, 0xff, %o0
        sll     %o0, 16, %o0
        or      %o0, %o2, %o2
        ldub    [%o1+3], %o0
        and     %o0, 0xff, %o0
        sll     %o0, 8, %o0
        or      %o0, %o2, %o2
        ldub    [%o1+4], %o0
        and     %o0, 0xff, %o0
        or      %o0, %o2, %o0
        add     %o0, 1, %o1
        sethi   %hi(.LLC0), %o0
        or      %o0, %lo(.LLC0), %o0
        call    printf, 0
         nop
        ld      [%fp-1052], %o0
        add     %o0, 1, %o0
        st      %o0, [%fp-1052]
        b       .LL2
         nop
.LL3:
        ret
        restore
.LLfe1:
        .size   main,.LLfe1-main
        .ident  "GCC: (GNU) 3.0.3"

어셈블리코드를 보면, 컴파일타임에 packed->b의 주소가 워드의 어디에
걸리는지를 모르니까 packed->b 의 0번째 바이트부터 세번째 바이트를
한바이트씩 읽고 (ldud, 아마 load unsigned byte 가 아닐까.. 찾아보지 않았습니다. ㅡ,.ㅡ) 적당히 AND, 쉬프트, OR 시켜서 다시 int 를 조립하는 방식을 쓰는군요.

아무래도 AND, shift, OR 연산이 오버헤드인 듯 싶어서 마지막으로 테스트 하나 더 해 봤습니다. 그냥 변수에 할당시키면 shift연산을 하지 않고 메모리대 메모리로 byte 단위로 할당을 할 수 있지 않을까 하고요.

#include <stdio.h>

typedef struct {
        char    a[1];
        int     b;
        char    c[2];
} __attribute__ ((packed)) packed_struct;

main()
{
        packed_struct   *packed;
        char    buf[1024];
        int     i;
        int     j;

        memset(&packed, 0, sizeof(packed));
        memset(buf, 0, 1024);

        for (i = 0; i < 4; i++) {
                packed = (packed_struct *)(buf + i);
                j = packed->b;
        }
}

어셈블리코드:

        .file   "malaligned3.c"
        .section        ".text"
        .align 4
        .global main
        .type   main,#function
        .proc   04
main:
        !#PROLOGUE# 0
        save    %sp, -1152, %sp
        !#PROLOGUE# 1
        add     %fp, -20, %o0
        mov     0, %o1
        mov     4, %o2
        call    memset, 0
         nop
        add     %fp, -1048, %o0
        mov     0, %o1
        mov     1024, %o2
        call    memset, 0
         nop
        st      %g0, [%fp-1052]
.LL2:
        ld      [%fp-1052], %o0
        cmp     %o0, 3
        ble     .LL5
        nop
        b       .LL3
         nop
.LL5:
        add     %fp, -1048, %o1
        ld      [%fp-1052], %o0
        add     %o1, %o0, %o0
        st      %o0, [%fp-20]
        ld      [%fp-20], %o1
        ldub    [%o1+1], %o0
        and     %o0, 0xff, %o0
        sll     %o0, 24, %o2
        ldub    [%o1+2], %o0
        and     %o0, 0xff, %o0
        sll     %o0, 16, %o0
        or      %o0, %o2, %o2
        ldub    [%o1+3], %o0
        and     %o0, 0xff, %o0
        sll     %o0, 8, %o0
        or      %o0, %o2, %o2
        ldub    [%o1+4], %o0
        and     %o0, 0xff, %o0
        or      %o0, %o2, %o0
        st      %o0, [%fp-1056]
        ld      [%fp-1052], %o0
        add     %o0, 1, %o0
        st      %o0, [%fp-1052]
        b       .LL2
         nop
.LL3:
        ret
        restore
.LLfe1:
        .size   main,.LLfe1-main
        .ident  "GCC: (GNU) 3.0.3"

그러나 그렇지는 않더군요. 이 경우에도 레지스터에 값을 만들어 놓은 후
한큐에 전달하네요.

임의의 메모리상 byte 대 byte의 move를 네번하는 것보다는
메모리와 레지스터에서 shift, AND, OR 하고 한번에 워드로 store하는게
더 빠르다고 판단했는지 어떤지는 모르겠습니다.

memcpy()가 함수호출 오버헤드가 있으니까

MEMCPYINT(t, s) 를 메크로 함수로 만들어서
((char *)(t))[0] = ((char *)(s))[0]
((char *)(t))[1] = ((char *)(s))[1]
((char *)(t))[2] = ((char *)(s))[2]
((char *)(t))[3] = ((char *)(s))[3]

값을 int 변수로 복사해서 사용하는 것이 나은지,
동일한 packed->b 를 여러번 접근하게 된다면 컴파일러가 임시값을 만들어서
접근을 최적화할지, 아니면 매번 load, shift, store 의 번거로운 작업을 계속할지 등
어떤 것이 나을지는 사용패턴과 사이클 계산 등 여러가지를 좀 고려해서
결정해야 할 듯 싶습니다.

일단 gcc가 각 플랫폼에서 유사하게 동작한다고 하면 __attribute__ ((packed)) 를 사용하면 문제는 해결될 것으로 보입니다.
그러나 원하는 성능상의 향상 (또는 성능저하 방지)이 있을지는 확신할 수 없을 듯 합니다.

최종호의 이미지

zingle wrote:

이번에 보니 snort와 같은 프로그램에서도 패킷 버퍼의 주소를 argument로 받아서 구조체를 그 위에 얹어서 decode하더군요.
(잠깐 본거라 다를지도 모릅니다. 비슷한 방식으로 디코드하는데 문제가 없는 걸 보면..--;;; :? )

말씀하신 메일링 리스트에서 한 것처럼
GCC의 __attribute__ ((packed)) 와 같은 컴파일러 옵션을 쓰지 않았을까요?

zingle의 이미지

자세한 분석, 감사드립니다.

덕분에 모르고 넘어갔을뻔한 내용들을 알게되었습니다. ^^

사실 주로 인텔 계열에서만 작업하고,
sparc쪽관련해서는 접근 가능한 소스가 별로 없어서
고민이었는데 이번에 많이 배운거 같습니다. :D

-------------

Snort를 확인해보니까 __attribute__((packed) 방법을 사용하지는 않았습니다.

특이한 것은 ip 헤더 중 맨 처음의 ip_v와 ip_hl 비트필드 중에서 ip_hl에는 접근이 되는데, ip_v에만 접근할수 가 없었습니다.
또한 중간에 값은 접근이 된느데 src/dst ip부분에만 접근이 안되구요.

그 주소 자체에 접근 안되는것은 아니니까
snort에서는 그런 부분들을 매크로 등을 사용해서 넘어가더군요.

zingle의 이미지

snort에서 사용하는 방법이 한가지 더 있군요.

preprocessor 중에 spp_frag2.c를 보면
메모리를 잡을때에 2바이트를 추가해서 할당 받습니다.
그리고 일단 맨 앞의 두바이트를 건너뛰게 포인터를 조정합니다.

이렇게 되면, ethernet 헤더의 길이는 여전히 14바이트이지만,
ip헤더는 offset이 16바이트가 되어버려서 alignment 문제가 발생하지 않습니다. (ip 헤더는 보통 20바이트이고 옵션이 붙는경우도 제가 알기로는 주로 4바이트 단위로 붙으니 일반적으로는 사용가능한 방법으로 보입니다.)

zingle의 이미지

alignment로 고민하는 김에 이왕에 다 해버리렵니다. ^^;;

gcc: 2.95.3 20010315 (release)
glib:1.2.10

# uname -a
SunOS dev_test 5.8 Generic_108528-09 sun4u sparc SUNW,Sun-Fire-280R

아래와 같은 코드를 SPARC 머신에서 컴파일하고 실행시키면
SIGBUS 에러가 발생합니다. invalid alignment 때문이지요.

#include <stdio.h>
#include <stdlib.h>

char data[] = {  /* random data */
"\x01\x01\x01\x01\x01\x01"
};

int main (int argc, char *argv[])
{

        printf("[address of data][%x]\n", data);
        printf("[value of (int)*data][%x]\n", (int)*(int *)data);
        printf("[address of data + 1][%x]\n", (data + 1));
        printf("[value of (int)*(data+1)][%x]\n", (int)*(int *)(data+1));
        return 1;

}

# ./struct_test 
[address of data][206e8]
[value of (int)*data][1010101]
[address of data + 1][206e9]
버스 오류(Bus Error) (core dumped)

처음 올렸던 질문에서는 구조체를 사용했었기에
구조체 선언부에 __attribute__((packed)) 옵션을 사용해서
해결해었습니다. 그러나 만약 캐스팅을 위와같이 int와 같은 type으로 받는다면
어떻게 해결할 수 있나요?

gcc의 옵션으로 제어가 가능한가요?

최종호의 이미지

#include <stdio.h> 
#include <stdlib.h> 

char data[] = {  /* random data */ 
        "\x01\x01\x01\x01\x01\x01" 
}; 

typedef int     __attribute__ ((aligned(1))) more_aligned_int;

int main (int argc, char *argv[]) 
{ 
        more_aligned_int        *maip;

        maip = (more_aligned_int *)(data + 1);
        printf("[value of i[%d]\n", *maip); 

        return 0; 
}

위 코드는 제 생각으로는 되어야 할 것 같은데 안되고 죽네요.

편법으로, struct 에 int 하나만 있는 것을 만들어서 이걸 통해서
접근하는 방법을 써야하는게 아닌가 생각이 드네요.

#include <stdio.h>
#include <stdlib.h>

char data[] = {  /* random data */
        "\x01\x01\x01\x01\x01\x01"
};

typedef struct {
        int     data;
} __attribute__ ((packed)) PACKED_INT;

int main (int argc, char *argv[])
{ 
        PACKED_INT        *ip;

        ip = (PACKED_INT *)(data + 1);
        printf("ip->data = %d\n", ip->data);
        
        return 0; 
}       
kyong의 이미지

__attribute__((packed))는 쓰는 것은 코드최적화를 방해하기 때문에
좋은 해결책이 아닌듯 합니다.
차라리 __attribute__((aligned))가 나을듯 합니다.
짐작에 -mbitfield-align 와 같은 옵션이 먹힌다면 그런 비슷한 일을 하지 않을까요?
unaligned access가 sparc에서는 바로 죽음이니까 또 피해가는 방법이 있는
것 같습니다. sparc이 없어서 전 테스트 못해봤습니다.

예제를 좀 더 현실적으로 고침됩니다.

#include <stdio.h>
#include <sys/types.h>

/* sparc */
/*
#define get_unaligned(ptr) \
  ({ __typeof__(*(ptr)) __tmp; memmove(&__tmp, (ptr), sizeof(*(ptr))); __tmp; })
*/

/* i386 */
#define get_unaligned(ptr) (*(ptr))

u_int8_t buf[2]= { "\x01\x02"};

int main (void)
{
    u_int8_t *p = &buf[0];
    u_int32_t data;

    /* unaligned method */
    /*
    data = *(u_int32_t *)p;
    */

    /* aligned method */
    data = get_unaligned(p);

    fprintf(stdout, "Now aligned?\n");
    fprintf(stdout, "data : %d\n", data);

    return(0);
}

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.