[英]Incompatible pointer type warning with pointer-to-pointer types when passing char** to void** function parameter
I'm trying to implement a secure-free function that erases the allocated memory, frees it and then also sets the pointer to the allocated region to NULL so the pointer cannot be reused after-free and cannot be double-freed with the same function. I'm trying to implement a secure-free function that erases the allocated memory, frees it and then also sets the pointer to the allocated region to NULL so the pointer cannot be reused after-free and cannot be double-freed with the same function . To achieve this I'm using a pointer-to-pointer parameter, which allows me to overwrite the pointer to the allocated memory.
为了实现这一点,我使用了一个指向指针的参数,它允许我覆盖指向分配的 memory 的指针。
The issue is GCC complaining about incompatible pointer types ("but it works on my machine");问题是 GCC 抱怨指针类型不兼容(“但它适用于我的机器”); I did not expect such a warning.
我没想到会有这样的警告。 My understanding was that any pointer can be implicitly cast to
void*
, thus I'm guessing also the address of a pointer could be cast to void**
.我的理解是任何指针都可以隐式转换为
void*
,因此我猜测指针的地址也可以转换为void**
。
In the meantime I rewrote secure_free()
as a macro, which solves the warning, but I would like to know why the compiler is complaining.与此同时,我将
secure_free()
重写为一个宏,它解决了警告,但我想知道编译器为什么抱怨。
File securefree.c
文件
securefree.c
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define STRING_BUFFER_LEN 10
/**
* Securely erases a heap-allocated memory section, frees it and sets its
* pointer to NULL to avoid use-after-free and double-free.
*/
static void secure_free(void** p_p_data, size_t length_in_bytes)
{
if (p_p_data == NULL || *p_p_data == NULL)
{ return; }
memset(*p_p_data, 0, length_in_bytes);
free(*p_p_data);
*p_p_data = NULL;
}
int main(void)
{
// Allocate some data
char* my_string = calloc(STRING_BUFFER_LEN, sizeof(char));
if (my_string == NULL) { return 1; }
// Use the allocated space in some way
my_string[0] = 'a';
my_string[1] = 'b';
// Free using the dedicated function
secure_free(&my_string, STRING_BUFFER_LEN);
return 0;
}
Compiling with GCC (Rev6, Built by MSYS2 project, 10.2.0):使用 GCC 编译(Rev6,由 MSYS2 项目构建,10.2.0):
$ gcc securefree.c -o securefree
securefree.c: In function 'main':
securefree.c:29:17: warning: passing argument 1 of 'secure_free' from incompatible pointer type [-Wincompatible-pointer-types]
29 | secure_free(&my_string, STRING_BUFFER_LEN);
| ^~~~~~~~~~
| |
| char **
securefree.c:11:32: note: expected 'void **' but argument is of type 'char **'
11 | static void secure_free(void** p_p_data, size_t length_in_bytes)
| ~~~~~~~^~~~~~~~
EDIT: the macro version looks like this编辑:宏版本看起来像这样
#define secure_free_macro(ptr, len) if ((ptr) != NULL) { \
memset((ptr), 0, (len)); free(ptr); (ptr) = NULL; }
What you're trying to do cannot be done portably, because different pointer types can have different representations;您尝试做的事情不能移植,因为不同的指针类型可以有不同的表示; and to assign the null pointer to the value, you must cast the pointer-to-pointer first to a pointer to the effective type of the actual pointer variable - which is impossible.
并将 null 指针分配给该值,您必须首先将指针指向指针转换为指向实际指针变量的有效类型的指针 - 这是不可能的。
What you can do however is use a macro, it is as good as any, and much simpler to use:但是,您可以做的是使用宏,它与任何宏一样好,并且使用起来更简单:
#define secure_free(x) (free(x), (x) = 0)
This works without &
.这在没有
&
的情况下有效。
C lets any pointer be implicitly cast to void*
as an explicit exception. C 允许将任何指针隐式转换为
void*
作为显式异常。 Note, that void
and char
are not compatible types.请注意,
void
和char
不是兼容的类型。 Thus void*
, char*
, void**
and char**
are not compatible as well.因此
void*
、 char*
、 void**
和char**
也不兼容。 That is why compiler emits a warning.这就是编译器发出警告的原因。
To bypass this issue change the function signature to use void*
:要绕过此问题,请将 function 签名更改为使用
void*
:
void secure_free(void* ptr, size_t length_in_bytes) {
void **p_p_data = (void**)ptr;
...
}
To add protection that the argument is a pointer to a pointer one could use a macro:要添加对参数是指向指针的指针的保护,可以使用宏:
#define secure_free(x,s) ((void)sizeof **(x), secure_free((x), (s)))
**(x)
will not compile is x were not a pointer to a pointer.**(x)
将无法编译。sizeof
prevent evaluation of x
in **(x)
to avoid side effects sizeof
防止在**(x)
中评估x
以避免副作用(void)
silence the compiler about complaining on unused value (void)
让编译器停止抱怨未使用的值(X,Y)
, return only value of the Y
, which is return value of secure_free(...)
(X,Y)
,仅返回Y
的值,即secure_free(...)
的返回值secure_free
as macro only if it used as a function.secure_free
扩展为宏。 This allows to use secure_free
as a pointer to a functionsecure_free
作为指向 function 的指针Extra note .额外说明。 In the code
在代码中
memset(*p_p_data, 0, length_in_bytes);
free(*p_p_data);
The compiler will likely optimize out memset()
.编译器可能会优化
memset()
。 I suggest casting to volatile void *
to force the compiler to generate clearing code.我建议强制转换为
volatile void *
以强制编译器生成清除代码。
Edit编辑
Additionally, one may have clear the content with a loop because memset
discards volatile
qualifier.此外,由于
memset
丢弃了volatile
限定符,因此可能会使用循环清除内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.