gpt4 book ai didi

java - 是否存在一种万无一失的跨平台方式来重现 SIGBUS?

转载 作者:可可西里 更新时间:2023-11-01 11:48:46 25 4
gpt4 key购买 nike

这个问题纯粹出于好奇;就我个人而言,我曾看到此信号被发出,但很少见。

我在 the C chatroom 上问过是否有可靠的方法来重现它。在这个房间里,user @Antti Haapala找到一个。至少在 Linux x86_64 系统上......经过一番摆弄之后,相同的模式可以用三种语言重现 - 然而,只能在基于 x86_64 Linux 的系统上,因为这些是唯一可以测试的系统......这是如何:

C

$ cat t.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main () {
int fd = open ("empty", O_RDONLY);
char *p = mmap (0, 40960, PROT_READ, MAP_SHARED, fd, 0);
printf("%c\n", p[4096]);
}
$ :>empty
$ gcc t.c
$ ./a.out
Bus error (core dumped)

python

$ cat t.py
import mmap
import re
import os

with open('empty', 'wb') as f:
f.write(b'a' * 4096)

with open('empty', 'rb') as f:
# memory-map the file, size 0 means whole file
mm = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)

os.system('truncate --size 0 empty')

b'123' in mm
$ python t.py
Bus error (core dumped)

Java

$ cat Test.java
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Random;

public final class Test
{
private static final int SIZE = 4096;
private static final Path VICTIM = Paths.get("/tmp/somefile");

public static void main(final String... args)
throws IOException
{
// Create our victim; delete it first if it already exsists
Files.deleteIfExists(VICTIM);
Files.createFile(VICTIM);

final Random rnd = new Random();
final byte[] contents = new byte[SIZE];
rnd.nextBytes(contents);
Files.write(VICTIM, contents);

try (
final FileChannel channel = FileChannel.open(VICTIM,
StandardOpenOption.READ, StandardOpenOption.WRITE);
) {
final MappedByteBuffer buffer
= channel.map(FileChannel.MapMode.READ_ONLY, 0L, SIZE);
channel.truncate(0L);
buffer.get(rnd.nextInt(SIZE));
}
}
}
$ javac Test.java
$ strace -ff -o TRACE java Test
Exception in thread "main" java.lang.InternalError: a fault occurred in a recent unsafe memory access operation in compiled Java code
at Test.main(Test.java:35)
fge@erwin:~/tmp$ grep -w SIGBUS TRACE.*
TRACE.15850:rt_sigaction(SIGBUS, NULL, {SIG_DFL, [], 0}, 8) = 0
TRACE.15850:rt_sigaction(SIGBUS, {0x7fe3db71b480, ~[RTMIN RT_1], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7fe3dc5d7d10}, {SIG_DFL, [], 0}, 8) = 0
TRACE.15850:--- SIGBUS {si_signo=SIGBUS, si_code=BUS_ADRERR, si_addr=0x7fe3dc9fb5aa} ---

再次声明:以上所有示例仅适用于 Linux x86_64 系统;我没有别的东西可以支配。

有没有办法在其他系统上重现它?

附带问题:如果上述示例在没有 SIGBUS 的系统上可重现,会发生什么?

最佳答案

SIGBUS 是使用内存映射文件的风险之一。 According to POSIX ,在以下条件下,您将获得关于 mmap() 的 SIGBUS:

The system shall always zero-fill any partial page at the end of an object. Further, the system shall never write out any modified portions of the last page of an object which are beyond its end. References within the address range starting at pa and continuing for len bytes to whole pages following the end of an object shall result in delivery of a SIGBUS signal.

An implementation may generate SIGBUS signals when a reference would cause an error in the mapped object, such as out-of-space condition.

在您的示例中,fd 引用的对象的长度为 0 字节。因此,映射的所有页面都是“对象末尾后的整页”,并在访问时生成 SIGBUS。

您可以通过不映射超过对象长度的页面来避免 SIGBUS。遗憾的是,MAP_SHARED 存在一个固有问题:即使在 mmap() 上验证了映射的长度是正确的,对象大小之后也可能会发生变化(例如,如果另一个进程在文件上调用 truncate()

通常,当您访问未映射的页面时,您总是会得到一个SIGBUS。在某些情况下,Linux 会生成 SIGSEGV,因为语义重叠。 SIGSEGV 应在以禁止方式访问页面时生成。

关于java - 是否存在一种万无一失的跨平台方式来重现 SIGBUS?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35899950/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com