- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
如您所知,可以捕获任何信号,但使用处理程序杀死并停止/计数。
存在三种无效地址访问:
我只对拒绝无效读取访问感兴趣。所以我的想法是捕获所有段错误,如果不是无效读取访问,则abort()
。
到目前为止,我只知道如何将 SEGV_MAPERR
和 SEGV_ACCERR
与 sigaction
一起使用,这当然是无关紧要的。
最佳答案
事实证明,在 x86-64(又名 AMD64)架构的 Linux 中,这实际上是非常可行的。
这是一个示例程序,crasher.c:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <ucontext.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#if !defined(__linux__) || !defined(__x86_64__)
#error This example only works in Linux on x86-64.
#endif
#define ALTSTACK_SIZE 262144
static const char hex_digit[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
static inline const char *signal_name(const int signum)
{
switch (signum) {
case SIGSEGV: return "SIGSEGV";
case SIGBUS: return "SIGBUS";
case SIGILL: return "SIGILL";
case SIGFPE: return "SIGFPE";
case SIGTRAP: return "SIGTRAP";
default: return "(unknown)";
}
}
static inline ssize_t internal_write(int fd, const void *buf, size_t len)
{
ssize_t retval;
asm volatile ( "syscall\n\t"
: "=a" (retval)
: "a" (1), "D" (fd), "S" (buf), "d" (len)
: "rcx", "r11" );
return retval;
}
static inline int wrerr(const char *p, const char *q)
{
while (p < q) {
ssize_t n = internal_write(STDERR_FILENO, p, (size_t)(q - p));
if (n > 0)
p += n;
else
if (n == 0)
return EIO;
else
return -n;
}
return 0;
}
static inline int wrs(const char *p)
{
if (p) {
const char *q = p;
while (*q)
q++;
return wrerr(p, q);
}
return 0;
}
static inline int wrh(unsigned long h)
{
static char buffer[4 + 2 * sizeof h];
char *p = buffer + sizeof buffer;
do {
*(--p) = hex_digit[h & 15];
h /= 16UL;
} while (h);
*(--p) = 'x';
*(--p) = '0';
return wrerr(p, buffer + sizeof buffer);
}
static void crash_handler(int signum, siginfo_t *info, void *contextptr)
{
if (info) {
ucontext_t *const ctx = (ucontext_t *const)contextptr;
wrs(signal_name(signum));
if (ctx->uc_mcontext.gregs[REG_ERR] & 16) {
const unsigned long sp = ctx->uc_mcontext.gregs[REG_RSP];
/* Instruction fetch */
wrs(": Bad jump to ");
wrh((unsigned long)(info->si_addr));
if (sp && !(sp & 7)) {
wrs(" probably by the instruction just before ");
wrh(*(unsigned long *)sp);
}
wrs(".\n");
} else
if (ctx->uc_mcontext.gregs[REG_ERR] & 2) {
/* Write access */
wrs(": Invalid write attempt to ");
wrh((unsigned long)(info->si_addr));
wrs(" by instruction at ");
wrh(ctx->uc_mcontext.gregs[REG_RIP]);
wrs(".\n");
} else {
/* Read access */
wrs(": Invalid read attempt from ");
wrh((unsigned long)(info->si_addr));
wrs(" by instruction at ");
wrh(ctx->uc_mcontext.gregs[REG_RIP]);
wrs(".\n");
}
}
raise(SIGKILL);
}
static int install_crash_handler(void)
{
stack_t altstack;
struct sigaction act;
altstack.ss_size = ALTSTACK_SIZE;
altstack.ss_flags = 0;
altstack.ss_sp = mmap(NULL, altstack.ss_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
if (altstack.ss_sp == MAP_FAILED) {
const int retval = errno;
fprintf(stderr, "Cannot map memory for alternate stack: %s.\n", strerror(retval));
return retval;
}
if (sigaltstack(&altstack, NULL)) {
const int retval = errno;
fprintf(stderr, "Cannot use alternate signal stack: %s.\n", strerror(retval));
return retval;
}
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
act.sa_sigaction = crash_handler;
if (sigaction(SIGSEGV, &act, NULL) == -1 ||
sigaction(SIGBUS, &act, NULL) == -1 ||
sigaction(SIGILL, &act, NULL) == -1 ||
sigaction(SIGFPE, &act, NULL) == -1) {
const int retval = errno;
fprintf(stderr, "Cannot install crash signal handlers: %s.\n", strerror(retval));
return retval;
}
return 0;
}
int main(int argc, char *argv[])
{
void (*jump)(void) = 0;
unsigned char *addr = (unsigned char *)0;
if (argc < 2 || argc > 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s call [ address ]\n", argv[0]);
fprintf(stderr, " %s read [ address ]\n", argv[0]);
fprintf(stderr, " %s write [ address ]\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (argc > 2 && argv[2][0] != '\0') {
char *end = NULL;
unsigned long val;
errno = 0;
val = strtoul(argv[2], &end, 0);
if (errno) {
fprintf(stderr, "%s: %s.\n", argv[2], strerror(errno));
return EXIT_FAILURE;
}
if (end)
while (*end == '\t' || *end == '\n' || *end == '\v' ||
*end == '\f' || *end == '\r' || *end == ' ')
end++;
if (!end || end <= argv[2] || *end) {
fprintf(stderr, "%s: Not a valid address.\n", argv[2]);
return EXIT_FAILURE;
}
jump = (void *)val;
addr = (void *)val;
}
if (install_crash_handler())
return EXIT_FAILURE;
if (argv[1][0] == 'c' || argv[1][0] == 'C') {
printf("Calling address %p: ", (void *)jump);
fflush(stdout);
jump();
printf("Done.\n");
} else
if (argv[1][0] == 'r' || argv[1][0] == 'R') {
unsigned char val;
printf("Reading from address %p: ", (void *)addr);
fflush(stdout);
val = *addr;
printf("0x%02x, done.\n", val);
} else
if (argv[1][0] == 'w' || argv[1][1] == 'W') {
printf("Writing 0xC4 to address %p: ", (void *)addr);
fflush(stdout);
*addr = 0xC4;
printf("Done.\n");
}
printf("No crash.\n");
return EXIT_SUCCESS;
}
使用例如编译它
gcc -Wall -O2 crasher.c -o crasher
您可以通过在命令行上指定操作和可选地址来测试对任意地址的调用、读取或写入。不带参数运行查看使用情况。
一些例子在我的机器上运行:
./crasher call 0x100
Calling address 0x100: SIGSEGV: Bad jump to 0x100 probably by the instruction just before 0x400c4e.
Killed
./crasher write 0x24
Writing 0xC4 to address 0x24: SIGSEGV: Invalid write attempt to 0x24 by instruction at 0x400bad.
Killed
./crasher read 0x16
Reading from address 0x16: SIGSEGV: Invalid read attempt from 0x16 by instruction at 0x400ca3.
Killed
./crasher write 0x400ca3
Writing 0xC4 to address 0x400ca3: SIGSEGV: Invalid write attempt to 0x400ca3 by instruction at 0x400bad.
Killed
./crasher read 0x400ca3
Reading from address 0x400ca3: 0x41, done.
No crash.
请注意,访问的类型是从((ucontext_t *)contextptr)->uc_mcontext.gregs[REG_ERR]
寄存器(来自信号处理程序上下文)中获得的;它与 arch/x86/mm/fault.c
in the Linux kernel sources 中定义的 x86_pf_error_code
枚举相匹配.
崩溃处理程序本身非常简单,只需要检查上述“寄存器”即可获取 OP 寻求的信息。
为了输出崩溃报告,我对 write()
系统调用进行了开放编码。 (由于某些原因,wrh()
函数所需的小缓冲区不能在堆栈上,所以我只是将其设为静态。)
我没有费心去实现 mincore()
系统调用来验证堆栈地址(crash_handler()
函数中的 sp
) );可能有必要避免双重故障(SIGSEGV
发生在 crash_handler()
本身)。
同样,我也懒得去打开 crash_handler()
末尾的 raise()
代码,因为如今在 x86-64 上它是在使用 tgkill(pid, tid, signum)
系统调用的 C 库,这意味着我还必须对 getpid()
和 gettid( )
系统调用。我只是懒惰。
最后,上面的代码写得很粗心,因为我自己是在与 OP user2284570 交换意见后才发现的,只是想把一些东西放在一起看看这种方法是否真的可靠。 (似乎是这样,但我只在一台机器上对此进行了轻微测试。)因此,如果您发现代码中有任何错误、错别字、想法或其他需要修复的问题,请在评论中告诉我,所以我可以修复它。
关于c - 从内部捕获 SIGSEGV 时,如何知道涉及的无效访问类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43033177/
这个问题不太可能对任何 future 的访客有帮助;它只与一个较小的地理区域、一个特定的时间点或一个非常狭窄的情况相关,通常不适用于全世界的互联网受众。如需帮助使此问题更广泛适用,visit the
我有一个在 ab 时间内运行的算法,其中 a 和 b 都是单独的输入。 我的算法还是多项式时间复杂度算法还是nn?我认为 nn 不是多项式,但我仍然不确定。 我看到 n 算法的阶乘仍然评估为 nn 复
这个问题在这里已经有了答案: Logical operators (AND, OR) with NA, TRUE and FALSE (2 个回答) 1年前关闭。 由于“is.na(NA)”返回真,“
假设我有一个具有以下结构的 Pandas 数据框: df = pd.DataFrame(dict(a=["x", "x", "y"], b=[0, 1, 1], c=[1, 2, 2])) 我想按 a
谁能帮我处理一些相当复杂的 Django 查询? 这些是我的模型: class County(models.Model): name = models.CharField(max_length
我想从某个表中选择一行并根据另一个表对结果进行排序。 这是我的表: lang1_words: word_id - word statuses: word_id - status 在每个表中 word_
我是单元测试的新手,所以请对我宽容一些。我有一些查询 RESTful API 的模块。我发现在每个测试套件中,我都使用几行代码来启动一个简单的 ExpressJS Web 服务器,以模拟一些我可以从测
假设我有以下代码: var blinker = function(element){ if(stopped){ return; } else { var sampleMappi
我正在用 JavaScript 制作一个选择你自己的冒险风格的游戏,在本节中: evade = prompt("Go out of your way to avoid them, just in ca
我的代码: import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Events1 extends
我正在使用 scipy ode 来解决钟摆问题。 from scipy import * import matplotlib.pyplot as plt from scipy.integrate im
我有一个 Google 表格,用于收集客户的注册数据。收集的数据包括学生的姓名、学生选择参加的类(class)以及信用卡号。提交后,我会收到通知。收到通知后,我会转到我的 Google 表格并从信用卡
我需要定义一个操作的两个版本,定义略有不同。它是一系列包含Nat指数的成分。 open import Data.Nat data Hom : ℕ → ℕ → Set where id : (
我正在研究游戏引擎 http://ducttape-dev.org使用 boost 作为依赖项之一。有一天,当我正在编写一个链接到我的游戏引擎的测试应用程序时,OgreProcedural 的 Ext
我正在 Android 中制作一个表达式计算器,所以我想在实际计算答案之前检查字符串是否符合有效表达式的条件。 我在 Java 中试过这个正则表达式: ^\s*([-+]?)(\d+)(?:\s*([
我有以下 postgresql 查询(为便于阅读而简化): select * from a_view where a in (select * from a_function(a_input))
我开始更好地掌握 PostgreSQL 索引,但我遇到了 OR 条件的问题,我不知道如何优化我的索引以加快查询速度。 我有 6 个条件,当单独运行时,它们的成本似乎很小。下面是修剪查询的示例,包括查询
有谁知道为什么下面的代码接受诸如123-123-1234这样的答案: [1-9]\\d{2}-[1-9]\\d{2}-\\d{4} 我想到了代码,它只接受先接受 2 个数字,再接受 2 个数字,然后再
在使用 Java 1.8u40 打开带有提示类型和附加的 StringConverter 的组合框时,我遇到了以下错误。这可以追溯到执行 FXML 的团队留下的示例字符串,与 Controller 中
在 MySQL 中,我有三个不同的数据库 - 我们将它们称为 A、B 和 C。 是否可以执行涉及所有三个数据库(A、B、C)中的表的事务? (所有数据库都在同一服务器上) 最佳答案 是的,你可以。这是
我是一名优秀的程序员,十分优秀!