В следующем фрагменте кода 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, и содержимому этого указателя нельзя доверять, но, насколько я понимаю, он должен указывать на какое-то место в стеке, а не на ноль? Я получаю следующий вывод в моей консоли. Можете ли вы объяснить, почему печать третьей строки (ноль) не 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

1
chandola 15 Дек 2019 в 14:44

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, который будет содержать результирующий указатель при возврате к вызывающей стороне.

Создание 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. При этом он предотвращает любое последующее злонамеренное использование указанного адреса (например, атака внедрения контента).

Я не вижу причин, по которым компилятор не должен это делать. Я уверен, что сторонник языковой чистоты подтвердит или опровергнет это подозрение.

2
WhozCraig 15 Дек 2019 в 12:14

Большинство современных компиляторов обнаруживают возвращение указателя на локальную переменную и фактически возвращают NULL (вообще говоря, они более агрессивны в отношении UB, и подход таков: пусть программа завершится неудачно как можно скорее или «сделает UB возможным для обнаружения времени выполнения», как в данном случае) https://godbolt.org/z/pDUXmm

2
P__J__ 15 Дек 2019 в 12:10