【问题标题】:C - How can a pointer overwrite a local const block of memory but not the global const block of memory?C - 指针如何覆盖本地 const 内存块而不是全局 const 内存块?
【发布时间】:2020-11-02 17:10:10
【问题描述】:

上次我问我如何绕过编译器不覆盖const 内存的承诺,在程序中生成segmentation 错误。用户Marco Bonelli 描述了以下方式,效果很好。

const static int global = 123;

int main(void) {
  int *ptr = (int *)&global;
  *ptr = 456;

  // or, equivalent
  const int *ptr2 = &global;
  *(int *)ptr2 = 456;
}

无论哪种方式,我都能产生分段错误。

  • int *ptr = (int *)&global;
    *ptr = 456;
    
  • const int *ptr2 = &global;
    *(int *)ptr2 = 456;
    

现在我的问题是是什么阻止了指针写入global const 内存块而不是local const 内存块。例如,在下面的代码中,我可以毫无问题地写入const 内存块。

#include <stdio.h>

int main(void) {
  const int local = 123;
  
  int *ptr = (int *)&local;
  *ptr = 456;
  
  // how come this be possible?
  printf("%d\n", local); // -> 456

  // or, equivalent
  const int *ptr2 = &local;
  *(int *)ptr2 = 512;

  // how come this be possible?
  printf("%d\n", local); // -> 512
}

我很想知道这是怎么发生的。请赐教。

如果重要的话,我正在使用gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

【问题讨论】:

  • 当你的程序终止时,这个“块”会发生什么——与编译器无关,也与编译器无关。这两个程序没有任何共同点,第一个程序的运行不应以任何方式影响第二个程序。该内存由操作系统回收。对于覆盖const 的问题 - 通常这是一个坏主意。它是 const 是有原因的,不应更改。
  • 你能解释一下你想要达到的目标吗?
  • 试图存储到声明为 const 的内存中会导致未定义的行为。
  • 您在这里问了几个不同的、不相关的问题。要点是一个进程中的物理内存完全独立于任何其他进程中的物理内存(假设您没有执行明确的mmap())。您可能想详细了解物理和逻辑内存空间之间的区别:en.wikipedia.org/wiki/Virtual_memory
  • 你想做什么?破解什么?在其脚下修改其他程序数据?还有什么?

标签: c pointers constants


【解决方案1】:

程序终止后,该块到底发生了什么?

您的进程的virtual memory 会发生什么由操作系统决定。当程序终止时,操作系统将清除为您的程序分配的所有内存,无论该内存用于什么用途。 const 的概念与此无关。

编译器是否能够从该特定块中删除该 const 限定符?我可以覆盖那块内存吗,如果可以,我该如何明确地做到这一点?

您无法更改变量的声明方式。它的整个生命周期都保持这种状态。你将无法做到这一点,即使那样,也可以通过 undefined behavior 尝试这样做。

如果我需要覆盖const 内存块怎么办?有什么办法吗?

如果您需要这样做,那么您编写的任何程序的逻辑都是有缺陷的。你不能也不应该这样做。这是未定义的行为,在最好的情况下会用 segmentation fault 杀死你的程序。


好的,如果你真的想通过写入const 变量导致的分段错误来终止程序,假设你的编译器将全局const 变量放在只读部分(例如.rodata ),那么以下内容就足够了:

const static int global = 123;

int main(void) {
    int *ptr = (int *)&global;
    *ptr = 456;

    // or, equivalent
    const int *ptr2 = &global;
    *(int *)ptr2 = 456;
}

您需要“抛弃”const 限定符,以便编译器不会将其视为错误。同样,这仅在编译器将global 放入只读部分(标准不要求)时才有效。如果这不会导致分段错误,那么这意味着您的编译器不会将所有 const 变量放在只读部分中。

【讨论】:

  • 是的,Marco 正是这就是我想说的我想用 segmentation fault 杀死我的程序我该怎么做。如果我尝试写入只读内存,它只会引发错误,但我想在运行时用分段错误杀死整个程序。
  • @Ayush 嗯好的,添加了一个例子。
  • 我正在尝试理解它,但我没有正确理解它,尽管我知道指针、多级指针、静态范围以及您在代码中使用的任何内容的概念,但您为什么尝试取消引用同一个内存块两次,而您已经写入该内存块一次,那么需要再次写入它。
  • @Ayush 阅读了我的评论,这只是一种不同的方式。这是一个例子,你不需要 id 两次。
  • 我想我需要多玩几次才能更好地理解,我会问你我是否会卡在某个地方。再次感谢。
【解决方案2】:

这完全取决于实施。例如,裸机 ARM uC 上的const 数据存储在闪存中。你可以写信到那里,但它根本没有效果。

托管系统的行为会因操作系统、其版本和硬件而异。

How can I overwrite a const block of memory 如果你想写就不要声明const。如果您确实尊重给予编译器的承诺。

如果只是初学者的好奇心,除了尝试之外别无他法。实验、调试并尝试解释结果。

编辑。

在最常见的实现中:

  • 本地常量(自动变量是在不受操作系统保护的堆栈上创建的 - 你没有得到段错误。

  • 常量全局(静态存储)变量被放置在受操作系统保护以防写入的 .rodata 段中 - 您遇到了段错误。

【讨论】:

  • 你没有错,你提出了一个重要的用例,即有问题的“内存”可能根本不是虚拟内存。但是.... OP 显然对一般的“内存”有一些基本的误解,特别是关于 C/C++ 编程结构“const”。在他/她能够正确解释“实验”之前,确保 OP 进行一些阅读并更好地理解更多“基础知识”可能是最重要的。恕我直言...
  • @paulsm4 我更新了我的问题。你能告诉我这是怎么发生的吗?
  • @Ayush 查看更新后的答案。
  • 有没有办法识别这样的实现?如果它违反了const 说明符的基本设计,为什么还会存在这样的实现?
  • @Ayush 这是未定义的行为 - 任何事情都可能发生。你对它的理解非常有限。请记住——永远不要通过做类似的事情来调用未定义的行为。
猜你喜欢
  • 1970-01-01
  • 2021-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-31
  • 2019-03-17
  • 2022-07-06
  • 1970-01-01
相关资源
最近更新 更多