Рассмотрим следующую программу из системы, которая позволяет пользователям запрашивать у системы записи об оценках в стенограмме студента:
int get_transcript_size(int student_id);
void load_transcript(int student_id, char* grades, int size);
int read_int();
char get_transcript_entry(int student_id, int entry) {
int size = get_transcript_size(student_id);
char grades[size];
load_transcript(student_id, grades, size);
return grades[entry];
}
int main() {
int student_id = 42; // not to be revealed to the user
int entry = read_int();
char grade = get_transcript_entry(student_id, entry);
printf("Requested grade: %c\n", grade);
return 0;
}
Код предполагает 3 предварительно реализованные функции:
1) get_transcript_size
: учитывая идентификатор студента, возвращает количество записей в его стенограмме
2) load_transcript
: загружает оценки в стенограмме студента в предварительно выделенный массив grades
соответствующего размера
3) read_int
, которыйпросит пользователя ввести целое число
Предполагается, что система никогда не раскрывает внутренний идентификатор учащегося, чьи оценки запрашивает пользователь (то есть 42).Тем не менее, код имеет потенциальную проблему безопасности.То есть злонамеренный пользователь может манипулировать системой, чтобы выявить идентификатор интервала учащегося с помощью оператора печати в функции main
, введя соответствующие значения int в систему с помощью функции read_int
Как мы можем объяснитьпроблема с использованием знаний о том, как сгенерированные компилятором последовательности вызовов управляют фактическими параметрами и локальными переменными функций в стеке?