为什么返回指向本地定义变量的指针为null而不是指向堆栈中位置的指针?

时间:2019-12-15 11:44:30

标签: c

在以下代码中,不应将str_s指向堆栈中的某个位置。

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

char* fun_s(){
    char str[8]="vikash";
    printf("s :%p\n", str);
    return str;
}

char* fun_h(){
    char* str = (char*)malloc(8);
    printf("h :%p\n", str);
    strcpy(str, "vikash");
    return str;
}

int main(){
    char* str_s = fun_s();
    char* str_h = fun_h();
    printf("s :%p\nh :%p\n", str_s, str_h);
    return 0;
}

我知道返回fun_s存在问题,并且不能信任此指针的内容,但是根据我的理解,它应该指向堆栈中的某个位置而不是零? 我在控制台中得到以下输出。您能解释一下为什么第三行打印(nil)而不是0x7ffce7561220

s :0x7ffce7561220
h :0x55c49538d670
s :(nil)
h :0x55c49538d670

GCC版本

gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

操作系统:Ubuntu 18.04.3 LTS

2 个答案:

答案 0 :(得分:2)

大多数现代编译器都会检测到返回指向局部变量的指针,并实际上返回NULL(通常来讲,对于UB更具攻击性,方法是-让程序尽快失败或“使UB能够检测运行时”这种情况)https://godbolt.org/z/pDUXmm

答案 1 :(得分:2)

您的编译器有意从该函数注入空返回值。我没有可用的gcc 7.4,但是我有7.3,我认为结果是相似的:

fun_s编译为汇编可实现以下目的:

.LC0:
        .string "s :%p\n"
fun_s:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        movabs  rax, 114844764957046
        mov     QWORD PTR [rbp-8], rax
        lea     rax, [rbp-8]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     eax, 0 ; ======= HERE =========
        leave
        ret

请注意将eax的硬设置为0,这将在返回到调用方时保留结果指针。

使str静态可实现以下目的:

.LC0:
        .string "s :%p\n"
fun_s:
        push    rbp
        mov     rbp, rsp
        mov     esi, OFFSET FLAT:str.2943
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     eax, OFFSET FLAT:str.2943
        pop     rbp
        ret

简而言之,您的编译器正在检测本地地址返回并将其重写为NULL。这样做可以防止以后再使用该地址(例如:内容注入攻击)。

我认为没有理由不允许编译器 这样做。我相信语言纯粹主义者会证实或拒绝这种怀疑。

相关问题