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

标签 java python c linux sigbus

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

我在 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/

相关文章:

python - 如何访问包装在sqlalchemy错误中的psycopg2错误

c - Vala/C 反序列化数组

java - 将 isPresent 替换为 ifPresent 和 orElse

java - Hibernate - 无法加载请求的类 : org. mariadb.jdbc.Driver

python - 计算管理值(value)的最有效方法

python - 什么时候安装 imgaug.py 我得到这个错误

c - ld 提示 : in function "_start", undefined reference to "__libc_csu_fini"& "__libc_csu_init"

c - 在 C 中反转链表

java - 信任库中信任哪些证书?

java - 常量引用的性能 - LIBGDX