C 언어 시큐어코딩 - Declare and Initialization 4
예약된 식별자를 선언하거나 정의하지 않는다.
밑줄과 대문자 또는 밑줄로 시작하는 모든 식별자는 항상 그 용도가 예약되어 있다. 예약된 식별자를 매크로 이름으로 정의하거나, 예약된 컨텍스트에서 식별자를 선언하거나 정의하지 않는다.
잘못된 코드 예제 (Include Guard)
C 표준 라이브러리에 헤더에 구현되어 정의된 이름이 없는 경우에도 컴파일러에 의해 암시적으로 미리 정의된 이름과 충돌할 수 있다.
#ifndef _MY_HEADER_H_
#define _MY_HEADER_H_
/* Contents of <my_header.h> */
#endif /* _MY_HEADER_H_ */
올바른 코드 예제 (Include Guard)
매크로 이름을 밑줄로 시작하지 않는다.
#ifndef MY_HEADER_H
#define MY_HEADER_H
/* Contents of <my_header.h> */
#endif /* MY_HEADER_H */
잘못된 코드 예제 (File Scope Objects)
_max_limit
은 정적이므로 충돌의 영향을 받지 않을 것으로 생각할 수 있으나, size_t를 정의하기 위해 포함된 이름들과 충돌할 가능성이 있으며 컴파일러는 예약된 이름을 암시적으로 선언할 수 있다.
_limit
은 런타임 라이브러리에 정의된 동일한 이름의 심볼과 충돌할 수 있다.
#include <stddef.h>
static const size_t _max_limit = 1024;
size_t _limit = 100;
unsigned int getValue(unsigned int count) {
return count < _limit ? count : _limit;
}
올바른 코드 예제 (File Scope Objects)
객체 이름을 밑줄로 시작하지 않는다.
#include <stddef.h>
static const size_t max_limit = 1024;
size_t limit = 100;
unsigned int getValue(unsigned int count) {
return count < limit ? count : limit;
}
잘못된 코드 예제 (Reserved Macros)
SIZE_MAX
라는 이름은 size_t
의 상한선을 나타내는 데 사용되는 표준 매크로 이름과 충돌한다. 또한 INTFAST16_LIMIT_MAX
라는 이름이 C 표준 라이브러리에 정의되어 있지는 않지만 INT
접두어로 시작하고 _MAX
접미사로 끝나기 때문에 예약된 식별자이다.
#include <inttypes.h>
#include <stdio.h>
static const int_fast16_t INTFAST16_LIMIT_MAX = 12000;
void print_fast16(int_fast16_t val) {
enum { SIZE_MAX = 80 };
char buf[SIZE_MAX];
if (INTFAST16_LIMIT_MAX < val) {
sprintf(buf, "The value is too large");
} else {
snprintf(buf, SIZE_MAX, "The value is %" PRIdFAST16, val);
}
}
올바른 코드 예제 (Reserved Macros)
예약된 접두사 및 접미사를 피해서 선언한다.
#include <inttypes.h>
#include <stdio.h>
static const int_fast16_t MY_INTFAST16_UPPER_LIMIT = 12000;
void print_fast16(int_fast16_t val) {
enum { BUFSIZE = 80 };
char buf[BUFSIZE];
if (MY_INTFAST16_UPPER_LIMIT < val) {
sprintf(buf, "The value is too large");
} else {
snprintf(buf, BUFSIZE, "The value is %" PRIdFAST16, val);
}
}
잘못된 코드 예제
malloc()
및 free()
표준 라이브러리 함수를 재정의하고 있다. 이 방법은 UNIX의 많은 전통적인 구현 방식으로 허용되지만, C 표준에 따라 문제가 발생할 수 있는 정의되지 않은 동작이다.
#include <stddef.h>
void *malloc(size_t nbytes) {
void *ptr;
/* Allocate storage from own pool and set ptr */
return ptr;
}
void free(void *ptr) {
/* Return storage to own pool */
}
올바른 코드 예제
C 표준 라이브러리 식별자를 재정의하지 않는다.
#include <stddef.h>
void *my_malloc(size_t nbytes) {
void *ptr;
/* Allocate storage from own pool and set ptr */
return ptr;
}
void *my_aligned_alloc(size_t alignment, size_t size) {
void *ptr;
/* Allocate storage from own pool, align properly, set ptr */
return ptr;
}
void *my_calloc(size_t nelems, size_t elsize) {
void *ptr;
/* Allocate storage from own pool, zero memory, and set ptr */
return ptr;
}
void *my_realloc(void *ptr, size_t nbytes) {
/* Reallocate storage from own pool and set ptr */
return ptr;
}
void my_free(void *ptr) {
/* Return storage to own pool */
}
잘못된 코드 예제 (errno)
errno
가 매크로인지 외부 Linkage로 선언된 식별자인지 불확실하다. 매크로 정의가 실제 객체에 액세스하지 못하거나 프로그램이 errno
라는 이름으로 식별자를 정의하면 문제가 발생할 수 있다.
extern int errno;
올바른 코드 예제 (errno)
errno
를 선언하는 올바른 방법은 헤더를 포함하는 것이다.
#include <errno.h>
예외들
라이브러리 함수가 헤더에 정의된 유형을 참조하지 않고 선언될 수 있는 경우, 선언이 표준 선언과 호환되면 헤더를 포함하지 않고 해당 함수를 선언할 수 있다.
/* Not including stdlib.h */
void free(void *);
void func(void *ptr) {
free(ptr);
}
선언이 stdlib.h
가 제공하는 것과 일치하고 예약된 식별자를 다시 정의하지 않기 때문에 이러한 코드는 호환된다. 그러나 이 예제에서 free()
함수에 대한 정의를 제공하는 것은 허용되지 않는다.
반복 실행해도 동작이 동일한 경우 다른 컴파일러 또는 언어 표준 모드와의 호환성을 위해 예약된 식별자와 동일한 매크로 식별자를 만들 수 있다.
/* Sometimes generated by configuration tools such as autoconf */
#define const const
/* Allowed compilers with semantically equivalent extension behavior */
#define inline __inline
컴파일러 벤더 또는 표준 라이브러리 개발자는 구현을 위해 예약 된 식별자를 사용할 수 있다.
/*
The following declarations of reserved identifiers exist in the glibc implementation of
<stdio.h>. The original source code may be found at:
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=include/stdio.h;hb=HEAD
*/
# define __need_size_t
# include <stddef.h>
/* Generate a unique file name (and possibly open it). */
extern int __path_search (char *__tmpl, size_t __tmpl_len,
const char *__dir, const char *__pfx,
int __try_tempdir);