Programing

포인터에 메모리 할당 여부 확인

crosscheck 2020. 11. 9. 07:48
반응형

포인터에 메모리 할당 여부 확인


함수에 전달 된 포인터가 C에서 메모리로 할당되었는지 여부를 확인할 수 있습니까?

- 난 내 자신의 캐릭터 포인터를 받아 C의 기능 wriiten 한 버피 [버퍼 포인터]와 크기 - buf_siz을 [버퍼 크기]. 실제로이 함수를 호출하기 전에 사용자는 버퍼를 생성하고 buf_siz의 메모리를 할당해야합니다.

사용자가 메모리 할당을 잊어 버릴 가능성이 있으므로 포인터를 내 함수에 전달하기 만하면됩니다. 따라서 전달 된 포인터가 실제로 buf_siz 메모리 양으로 할당되었는지 확인하기 위해 함수를 확인할 수있는 방법이 있습니까?

EDIT1 : 그것을 검사 할 표준 라이브러리가없는 것 같습니다. .. 그러나 그것을 검사 할 더러운 해킹이 있습니까 .. ??

EDIT2 : 나는 내 기능이 좋은 C 프로그래머에 의해 사용될 것이라는 것을 알고있다. 그러나 나는 우리가 확인할 수 있는지 여부를 알고 싶다. .. 우리가 그것을 듣고 싶을 경우 ..

결론 : 따라서 특정 포인터에 메모리가 할당되어 있는지 여부를 함수 내에서 확인할 수 없습니다.


일부 구현 특정 해킹을 제외하고는 확인할 수 없습니다.

포인터는 그들이 가리키는 위치 외에는 정보가 없습니다. 최선의 방법은 "이 특정 컴파일러 버전이 메모리를 할당하는 방법을 알고 있으므로 메모리를 역 참조하고 포인터를 4 바이트 뒤로 이동하고 크기를 확인하고 일치하는지 확인합니다 ..."라고 말하는 것입니다. 메모리 할당은 구현이 정의되어 있으므로 표준 방식으로 수행 할 수 없습니다. 그들은 그것을 전혀 동적으로 할당하지 않았을 수도 있습니다.

클라이언트가 C로 프로그래밍하는 방법을 알고 있다고 가정하면됩니다. 제가 생각할 수있는 유일한 해결 방법은 메모리를 직접 할당하고 반환하는 것입니다.하지만 이것은 작은 변화가 아닙니다. (더 큰 디자인 변경입니다.)


플랫폼 별 솔루션의 경우 Win32 기능 IsBadReadPtr(및 이와 유사한 다른 기능)에 관심이있을 수 있습니다 . 이 함수는 특정 메모리 청크에서 읽을 때 분할 오류가 발생할지 여부를 (거의) 예측할 수 있습니다.

그러나 운영 체제는 C 런타임 힙 관리자를 전혀 모르고 호출자가 예상만큼 크지 않은 버퍼를 전달하면 나머지 힙 블록을 전달하므로 일반적인 경우에는 보호 되지 않습니다. OS 관점에서 계속 읽을 수 있습니다.


아래 코드는 일부 포인터가 불법 메모리에 액세스하려고 시도하는지 확인하기 위해 한 번 사용한 것입니다. 메커니즘은 SIGSEGV를 유도하는 것입니다. SEGV 신호는 이전에 프로그램으로 돌아 가기 위해 longjmp를 사용하는 private 함수로 리디렉션되었습니다. 일종의 해킹이지만 작동합니다.

코드는 개선 될 수 있지만 ( 'signal'대신 'sigaction'사용) 아이디어를 제공하는 것입니다. 또한 다른 Unix 버전으로 이식 가능합니다. Windows의 경우 확실하지 않습니다. SIGSEGV 신호는 프로그램의 다른 곳에서 사용해서는 안됩니다.

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

jmp_buf jump;

void segv (int sig)
{
  longjmp (jump, 1); 
}

int memcheck (void *x) 
{
  volatile char c;
  int illegal = 0;

  signal (SIGSEGV, segv);

  if (!setjmp (jump))
    c = *(char *) (x);
  else
    illegal = 1;

  signal (SIGSEGV, SIG_DFL);

  return (illegal);
}

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

  i = malloc (1);

  if (memcheck (i))
    printf ("i points to illegal memory\n");
  if (memcheck (j))
    printf ("j points to illegal memory\n");

  free (i);

  return (0);
}

64 비트 솔라리스에서 더러운 해킹을 사용한 적이 있습니다. 64 비트 모드에서 힙은 0x1 0000 0000에서 시작합니다. 포인터를 비교하여 데이터 또는 코드 세그먼트 p < (void*)0x100000000의 포인터 p > (void*)0x100000000인지 , 힙 의 포인터인지 또는 메모리 매핑 영역의 포인터인지 확인할 수 있습니다 (intptr_t)p < 0(mmap은 맨 위의 주소를 반환합니다. 주소 지정 가능 영역). 이를 통해 내 프로그램에서 할당 된 포인터와 메모리 매핑 된 포인터를 동일한 맵에 유지하고 맵 모듈에서 올바른 포인터를 해제 할 수 있습니다.

하지만 이런 종류의 트릭은 이식이 매우 어렵고 코드가 이와 같은 것에 의존한다면 코드의 아키텍처를 재고 할 때입니다. 당신은 아마도 뭔가 잘못하고있을 것입니다.


나는 항상 포인터를 null 값으로 초기화합니다. 따라서 메모리를 할당하면 변경됩니다. 메모리가 할당되었는지 확인하면 pointer != NULL. 메모리 할당을 해제 할 때 포인터도 null로 설정합니다. 충분한 메모리가 할당되었는지 알 수있는 방법이 없습니다.

이것은 당신의 문제를 해결하지 못하지만 누군가가 C 프로그램을 작성한다면 그는 그것을 제대로 할 수있을만큼 충분히 숙련되어 있다고 믿어야합니다.


나는이 오래된 질문 알아요,하지만 몇 가지 hackish 솔루션은 이미 여기에있다 거의 모든이 C로 가능하지만 메모리가 올바르게 할당 된 경우 결정하는 유효한 방법의 자리를 차지할 오라클을 사용하는 것입니다 malloc, calloc, realloc, 및 free. 이것은 테스트 프레임 워크 (예 : cmocka)가 메모리 문제 (예 : 메모리를 해제하지 않는 오류 등)를 감지 할 수있는 것과 동일한 방법입니다. 할당 된 메모리 주소 목록을 그대로 유지하고 사용자가 함수를 사용하고 싶을 때이 목록을 확인하면됩니다. 내 자신의 테스트 프레임 워크에 대해 매우 유사한 것을 구현했습니다. 몇 가지 예제 코드 :

typedef struct memory_ref {
    void       *ptr;
    int        bytes;
    memory_ref *next;
}

memory_ref *HEAD = NULL;

void *__wrap_malloc(size_t bytes) {
    if(HEAD == NULL) {
        HEAD = __real_malloc(sizeof(memory_ref));
    }

    void *tmpPtr = __real_malloc(bytes);

    memory_ref *previousRef = HEAD;
    memory_ref *currentRef = HEAD->next;
    while(current != NULL) {
        previousRef = currentRef;
        currentRef = currentRef->next;
    }

    memory_ref *newRef = (memory_ref *)__real_malloc(sizeof(memory_ref));
    *newRef = (memory_ref){
        .ptr   = tmpPtr,
        .bytes = bytes,
        .next  = NULL
    };

    previousRef->next = newRef;

    return tmpPtr;
}

당신은 유사한 기능을 할 것이다 calloc, realloc그리고 free,로 시작 각각의 래퍼 __wrap_. 실제 malloc__real_malloc(랩핑하는 다른 기능과 유사)를 사용하여 사용할 수 있습니다 . 메모리가 실제로 할당되었는지 확인하려면 연결 memory_ref목록을 반복 하고 메모리 주소를 찾으면됩니다. 만약 당신이 그것을 찾았고 그것이 충분히 크다면, 당신은 확실히 메모리 주소가 당신의 프로그램을 충돌시키지 않을 것이라는 것을 알고있을 것입니다. 그렇지 않으면 오류를 반환합니다. 프로그램이 사용하는 헤더 파일에 다음 행을 추가합니다.

extern void *__real_malloc  (size_t);
extern void *__wrap_malloc  (size_t);
extern void *__real_realloc (size_t);
extern void *__wrap_realloc (size_t);
// Declare all the other functions that will be wrapped...

내 요구 사항은 매우 간단했기 때문에 매우 기본적인 구현을 구현했지만 더 나은 추적 시스템을 갖도록 확장 할 수있는 방법을 상상할 수 있습니다 (예 : struct크기와 함께 메모리 위치를 추적 하는 생성 ). 그런 다음 간단히 코드를 컴파일합니다.

gcc src_files -o dest_file -Wl,-wrap,malloc -Wl,-wrap,calloc -Wl,-wrap,realloc -Wl,-wrap,free

단점은 사용자가 위의 지시문으로 소스 코드를 컴파일해야한다는 것입니다. 그러나 그것은 내가 본 더 나쁜 것과는 거리가 멀다. 메모리를 할당하고 해제하는 데 약간의 오버 헤드가 있지만 보안을 추가 할 때는 항상 약간의 오버 헤드가 있습니다.


아니요, 일반적으로이를 수행 할 방법이 없습니다.

또한 인터페이스가 "내가 물건을 넣을 버퍼에 대한 포인터를 전달"하는 경우 호출자는 메모리를 전혀 할당 하지 않고 대신 정적으로 할당 된 고정 크기 버퍼 나 자동 변수 등을 사용할 수 있습니다. 또는 힙에있는 더 큰 개체의 일부에 대한 포인터 일 수도 있습니다.

인터페이스가 특별히 "할당 된 메모리에 대한 포인터를 전달합니다 (할당을 해제 할 것이므로)"라고 말하면 호출자가 그렇게 할 것이라고 예상해야합니다. 그렇게하지 않으면 확실하게 감지 할 수 없습니다.


시도 할 수있는 한 가지 해킹은 포인터가 할당 된 메모리를 스택을 가리키는 지 확인하는 것입니다. 할당 된 버퍼가 작거나 포인터가 일부 전역 메모리 섹션 (.bss, .const, ...)을 가리킬 수 있으므로 일반적으로 도움이되지 않습니다.

이 해킹을 수행하려면 먼저 main ()에 첫 번째 변수의 주소를 저장합니다. 나중에이 주소를 특정 루틴의 지역 변수 주소와 비교할 수 있습니다. 두 주소 사이의 모든 주소는 스택에 있습니다.


표준 C에서 사용할 수있는 어떤 것도 확인할 수 없습니다. 특정 컴파일러가이를 수행하는 함수를 제공한다고해도 여전히 나쁜 생각입니다. 이유에 대한 예는 다음과 같습니다.

int YourFunc(char * buf, int buf_size);

char str[COUNT];
result = YourFunc(str, COUNT);

다른 사람들이 말했듯이 표준 방법은 없습니다.

지금까지 스티브 맥과이어의 ' 솔리드 코드 작성 '에 대해 언급 한 사람은 아무도 없습니다 . 비록 몇 분기 에 걸쳐 거세되었지만 ,이 책은 메모리 관리에 관한 장을 가지고 있으며, 프로그램의 모든 메모리 할당을주의 깊게 완전히 제어하여 주어진 포인터가 올바른지 여부를 묻고 결정할 수있는 방법을 논의합니다. 동적으로 할당 된 메모리에 대한 유효한 포인터. 그러나 타사 라이브러리를 사용하려는 경우 일부 라이브러리를 사용하여 메모리 할당 루틴을 자신의 것으로 변경할 수 있다는 사실을 알게 될 것이므로 이러한 분석이 매우 복잡해집니다.


라이브러리 호출에서 수행하는 방법을 모르겠지만 Linux에서는 /proc/<pid>/numa_maps. 메모리의 모든 섹션이 표시되고 세 번째 열에는 "힙"또는 "스택"이 표시됩니다. 원시 포인터 값을보고 정렬 된 위치를 볼 수 있습니다.

예:

00400000 prefer:0 file=/usr/bin/bash mapped=163 mapmax=9 N0=3 N1=160
006dc000 prefer:0 file=/usr/bin/bash anon=1 dirty=1 N0=1
006dd000 prefer:0 file=/usr/bin/bash anon=9 dirty=9 N0=3 N1=6
006e6000 prefer:0 anon=6 dirty=6 N0=2 N1=4
01167000 prefer:0 heap anon=122 dirty=122 N0=25 N1=97
7f39904d2000 prefer:0 anon=1 dirty=1 N0=1
7f39904d3000 prefer:0 file=/usr/lib64/ld-2.17.so anon=1 dirty=1 N0=1
7f39904d4000 prefer:0 file=/usr/lib64/ld-2.17.so anon=1 dirty=1 N1=1
7f39904d5000 prefer:0 anon=1 dirty=1 N0=1
7fffc2d6a000 prefer:0 stack anon=6 dirty=6 N0=3 N1=3
7fffc2dfe000 prefer:0

따라서 0x01167000 이상이지만 0x7f39904d2000 미만인 포인터는 힙에 있습니다.


아니, 할 수 없습니다. 표준 라이브러리 나 다른 곳에서는이 작업을 수행하는 함수가 없음을 알 수 있습니다. 표준적인 방법이 없기 때문입니다. 호출 코드는 메모리를 올바르게 관리하는 책임을 받아 들여야합니다.


일반적으로 lib 사용자는 입력 확인 및 확인을 담당합니다. lib 코드에서 ASSERT 또는 무언가를 볼 수 있으며 디버그 용으로 만 사용됩니다. C / C ++를 작성할 때 표준 방식입니다. 많은 코더들이 lib 코드를 매우 신중하게 확인하고 확인하는 것을 좋아합니다. 정말 "나쁜"습관. IOP / IOD에 명시된 바와 같이 lib 인터페이스는 계약이어야하며 lib가 할 일과하지 않을 일, lib 사용자가해야 할 일과 필요하지 않은 일을 명확히해야합니다.


초기화되지 않은 포인터는 정확히 초기화되지 않은 것입니다. 어떤 것을 가리 키거나 단순히 유효하지 않은 주소 일 수 있습니다 (즉, 물리적 또는 가상 메모리에 매핑되지 않은 주소).

실용적인 해결책은 가리키는 객체에 유효성 서명을 갖는 것입니다. 요청 된 블록 크기와 서명 구조의 크기를 할당하고 블록의 시작 부분에 서명 구조를 생성하지만 서명 뒤의 위치에 대한 포인터를 반환하는 malloc () 래퍼를 만듭니다. 그런 다음 포인터를 가져오고 음의 오프셋을 사용하여 유효성 구조를 가져 와서 확인하는 유효성 검사 함수를 만들 수 있습니다. 물론 유효성 서명을 덮어 써서 블록을 무효화하고 할당 된 블록의 실제 시작에서 해제를 수행하려면 해당 free () 래퍼가 필요합니다.

유효성 구조로 블록의 크기와 보완 구조를 사용할 수 있습니다. 이렇게하면 블록의 유효성을 검사 할 수있을뿐만 아니라 (두 값을 XOR하고 0과 비교) 블록 크기에 대한 정보도 얻을 수 있습니다.


이를 수행하는 간단한 방법이 있습니다. 포인터를 만들 때마다 그 주위에 래퍼를 작성하십시오. 예를 들어, 프로그래머가 라이브러리를 사용하여 구조를 만드는 경우입니다.

struct struct_type struct_var;

그가 다음과 같은 기능을 사용하여 메모리를 할당하는지 확인하십시오.

struct struct_type struct_var = init_struct_type()

예를 들어이 struct_var에 동적으로 할당 된 메모리가 포함되어 있으면

struct_type의 정의가

typedef struct struct_type {
 char *string;
}struct_type;

그런 다음 init_struct_type () 함수에서 다음을 수행하십시오.

init_struct_type()
{ 
 struct struct_type *temp = (struct struct_type*)malloc(sizeof(struct_type));
 temp->string = NULL;
 return temp;
}

이렇게하면 temp-> string을 값에 할당하지 않는 한 NULL로 유지됩니다. 문자열이 NULL이든 아니든이 구조를 사용하는 함수를 체크인 할 수 있습니다.

한 가지 더, 프로그래머가 너무 나빠서 함수를 사용하지 못하고 할당되지 않은 메모리에 직접 액세스하는 경우 라이브러리를 사용할 자격이 없습니다. 문서에 모든 것이 명시되어 있는지 확인하십시오.


다음과 같은 방법으로 호출 할 수 있습니다 malloc_size(my_ptr)에서 malloc/malloc.h이 포인터가 할당되지 않은 경우 크기의 malloc이 포인터를 0 당신을 위해 할당 한 반환합니다. malloc은 할당 된 블록의 크기를 조정하여 가장 제한적인 유형 변수를 해당 포인터에서 역 참조하고 메모리를 정렬 할 수 있도록합니다. 따라서 malloc (1) (및 malloc (0))을 호출하면 가장 제한적인 유형의 크기가 16 바이트이므로 malloc은 실제로 16 바이트 (대부분의 컴퓨터에서)를 반환합니다.


포인터 추적기, 포인터의 유효성을 추적 및 확인합니다.

용법:

메모리 생성 int * ptr = malloc (sizeof (int) * 10);

추적기 Ptr (& ptr);에 포인터 주소 추가

실패한 포인터 확인 PtrCheck ();

코드 끝에서 모든 추적기를 해제하십시오.

PtrFree ();

 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stdbool.h>

struct my_ptr_t { void ** ptr; size_t mem; struct my_ptr_t *next, *previous; };

static struct my_ptr_t * ptr = NULL;

void Ptr(void * p){ 
                struct my_ptr_t * tmp = (struct my_ptr_t*) malloc(sizeof(struct my_ptr_t));
                printf("\t\tcreating Ptr tracker:");    
                if(ptr){ ptr->next = tmp; }
                tmp->previous = ptr;
                ptr = tmp;
                ptr->ptr = p;
                ptr->mem = **(size_t**) ptr->ptr;
                ptr->next = NULL;
                printf("%I64x\n", ptr); 
};
void PtrFree(void){
                    if(!ptr){ return; }
                    /* if ptr->previous == NULL */
                    if(!ptr->previous){ 
                                    if(*ptr->ptr){
                                                free(ptr->ptr);
                                                ptr->ptr = NULL;
                                    }
                                    free(ptr);
                                    ptr = NULL; 
                            return;                 
                    }

                    struct my_ptr_t * tmp = ptr;    
                    for(;tmp != NULL; tmp = tmp->previous ){
                                            if(*tmp->ptr){
                                                        if(**(size_t**)tmp->ptr == tmp->mem){
                                                                                                                                                    free(*tmp->ptr);
                                                                        *tmp->ptr = NULL;
                                                        }
                                            }
                                        free(tmp);
                    } 
            return; 
};

void PtrCheck(void){
                if(!ptr){ return; }
                if(!ptr->previous){
                        if(*(size_t*)ptr->ptr){
                                    if(*ptr->ptr){
                                                if(**(size_t**) ptr->ptr != ptr->mem){
                                                                printf("\tpointer %I64x points not to a valid memory address", ptr->mem);
                                                                printf(" did you freed the memory and not NULL'ed the pointer or used arthmetric's on pointer %I64x?\n", *ptr->ptr);
                                                                return; 
                                                        }   
                                    }
                                    return;
                                }
                        return;
                }
                struct my_ptr_t * tmp = ptr;
                for(;tmp->previous != NULL; tmp = tmp->previous){   
                                if(*(size_t*)tmp->ptr){         
                                                   if(*tmp->ptr){
                                                            if(**(size_t**) tmp->ptr != tmp->mem){
                                                                        printf("\tpointer %I64x points not to a valid memory address", tmp->mem);
                                                                        printf(" did you freed the memory and not NULL'ed the pointer or used arthmetric's on pointer %I64x?\n", *tmp->ptr);                            continue;
                                                            } 
                                                    }
                                                    continue;
                                }

                } 
            return;
       };

 int main(void){
        printf("\n\n\t *************** Test ******************** \n\n");
        size_t i = 0;
        printf("\t *************** create tracker ********************\n");
        int * ptr = malloc(sizeof(int) * 10);
        Ptr(&ptr);
        printf("\t *************** check tracker ********************\n");
        PtrCheck();
        printf("\t *************** free pointer ********************\n");
        free(ptr);
        printf("\t *************** check tracker ********************\n");
        PtrCheck();
        printf("\t *************** set pointer NULL *******************\n");
        ptr = NULL;
        printf("\t *************** check tracker ********************\n");
                PtrCheck();
        printf("\t *************** free tracker ********************\n");
        PtrFree();
        printf("\n\n\t *************** single check done *********** \n\n");
        printf("\n\n\t *************** start multiple test *********** \n");
        int * ptrs[10];
        printf("\t *************** create trackers ********************\n");
        for(; i < 10; i++){
                        ptrs[i] = malloc(sizeof(int) * 10 * i);
                        Ptr(&ptrs[i]);
                 }
        printf("\t *************** check trackers ********************\n");
        PtrCheck();
        printf("\t *************** free pointers but set not NULL *****\n");
        for(i--; i > 0; i-- ){ free(ptrs[i]); }
        printf("\t *************** check trackers ********************\n");
        PtrCheck(); 
        printf("\t *************** set pointers NULL *****************\n");
        for(i=0; i < 10; i++){ ptrs[i] = NULL; }
        printf("\t *************** check trackers ********************\n");
        PtrCheck(); 
        printf("\t *************** free trackers ********************\n");
        PtrFree();
        printf("\tdone");
    return 0;
 }

컴퓨터에는 거의 "절대"가 없습니다. 크로스 플랫폼은 예상보다 훨씬 많습니다. 25 년 후 저는 크로스 플랫폼을 예상하는 수백 개의 프로젝트에서 작업했지만 실현되지 않았습니다.

분명히 스택의 변수는 거의 선형 인 스택의 영역을 가리 킵니다. 크로스 플랫폼 가비지 수집기는 스택의 상단 또는 (하단)을 표시하고 약간의 함수를 호출하여 스택이 위아래로 커지는 지 확인한 다음 스택 포인터를 확인하여 스택의 크기를 확인하는 방식으로 작동합니다. 이것은 당신의 범위입니다. 이런 방식으로 스택을 구현하지 않는 머신 (성장 또는 감소)을 모르겠습니다.

You simply check if the address of our object or pointer sits between the top and bottom of the stack. This is how you would know if it is a stack variable.

Too simple. Hey, is it correct c++? No. Is correct important? In 25 years I have seen way more estimation of correct. Well, let's put it this way: If you are hacking, you aren't doing real programming, you are probably just regurigating something that's already been done.

How interesting is that?


Well, I don't know if somebody didn't put it here already or if it will be a possibility in your programme. I was struggling with similar thing in my university project.

I solved it quite simply - In initialization part of main() , after I declared LIST *ptr, I just put that ptr=NULL. Like this -

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

LIST *ptr;
ptr=NULL;

So when allocation fails or your pointer isn't allocated at all, it will be NULL. SO you can simply test it with if.

if (ptr==NULL) {
  "THE LIST DOESN'T EXIST" 
} else {
  "THE LIST MUST EXIST --> SO IT HAS BEEN ALLOCATED"
}

I don't know how your programme is written, but you surely understand what am I trying to point out. If it is possible to check like this your allocation and then pass your arguments to you function, you could have a simple solution.

Of course you must be careful to have your functions with allocating and creating the structure done well but where in C you don't have to be careful.

참고URL : https://stackoverflow.com/questions/1576300/checking-if-a-pointer-is-allocated-memory-or-not

반응형