warning: ‘strcpy’ writing one too many bytes into a region of a size that depends on ‘strlen’
警告:strlen()で確保した領域は終端文字’¥0’の考慮が足りない
[-Wstringop-overflow=]
■この記事の概要
この記事では、C言語でmalloc(strlen)
を使う際、終端文字(ヌル文字)を考慮しないとメモリ破壊が起こる問題について解説しています。
strcpy
関数を使用した具体例や、適切な修正方法が紹介されています。
■終端文字’¥0’の1byte足りない(heap overflow)
//gcc -fsanitize=address -g で潜在バグが表面化する
void I_hate_bug(void){
char *str = malloc(strlen(__func__));
if(str == NULL){
perror(NULL);
return ;
}
strcpy(str,__func__);
puts(str);
free(str);
}
例えば
char *str = malloc(strlen(“abc”));
strlen(“abc”)の結果は3となり
終端文字’¥0’の分は
malloc()で確保されません。
strcpy()は終端文字’¥0’までコピーするので
strの領域は1byte足りずメモリ破壊されます。
■熱心すぎる修正例(非推奨)
void I_love_casts(void){
char *str ;
size_t len = strlen(__func__)+sizeof('\0');
str = (char *)malloc(sizeof(char)*len);
if((char *)NULL == str){
perror(NULL);
return ;
}
strcpy(str,__func__);
puts(str);
free(str);
}
(1)C言語では(char *)mallocのキャストが不要です。
C++とは違います。
(2)C言語ではsizeof(‘\0’)の結果は1ではありません。
C++とは違います。
(3)C言語ではsizeof(char)の結果は1と決まっているので掛け算は不要です。
(4)NULLにキャストは不要です。
参考
http://www.kouno.jp/home/c_faq/c7.html#7
http://www.kouno.jp/home/c_faq/c7.html#8
●推奨修正例1
void I_hate_casts(void){
char *str = malloc(strlen(__func__)+1);
if(str == NULL){
perror(NULL);
return ;
}
strcpy(str,__func__);
puts(str);
free(str);
}
●推奨修正例2(strdupを使う)
void I_love_Simple(void){
char *str = strdup(__func__);//標準関数を使う
if(str == NULL){
perror(NULL);
return ;
}
strcpy(str,__func__);
puts(str);
free(str);
}
malloc(strlen(func)+1)の1が
マジックナンバーで気になる人は
標準関数strdup()を使うと良いでしょう。
■サンプルコードのまとめ
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//終端文字’¥0’の1バイト足りない
//gcc -fsanitize=address -g で潜在バグが表面化する
void I_hate_bug(void){
char *str = malloc(strlen(__func__));
if(str == NULL){
perror(NULL);
return ;
}
strcpy(str,__func__);
puts(str);
free(str);
}
//熱心すぎる修正例(非推奨)
//(1)C言語では(char *)mallocのキャストが不要。C++と違う
//(2)C言語ではsizeof('\0')の結果は1ではない。C++と違う
//(3)C言語ではsizeof(char)の結果は1と決まっているので掛け算不要
//(4)NULLにキャスト不要
void I_love_casts(void){
char *str ;
size_t len = strlen(__func__)+sizeof('\0');
str = (char *)malloc(sizeof(char)*len);
if((char *)NULL == str){
perror(NULL);
return ;
}
strcpy(str,__func__);
puts(str);
free(str);
}
//修正例
void I_hate_casts(void){
char *str = malloc(strlen(__func__)+1);
if(str == NULL){
perror(NULL);
return ;
}
strcpy(str,__func__);
puts(str);
free(str);
}
//推奨修正例
void I_love_Simple(void){
char *str = strdup(__func__);//標準関数を使う
if(str == NULL){
perror(NULL);
return ;
}
strcpy(str,__func__);
puts(str);
free(str);
}
int main(void){
I_hate_bug();
I_love_casts();
I_hate_casts();
I_love_Simple();
}