编程语言
首页 > 编程语言> > java – 有没有一种万无一失的跨平台方式来重现SIGBUS?

java – 有没有一种万无一失的跨平台方式来重现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.

标签:c-3,java,python,linux,sigbus
来源: https://codeday.me/bug/20190829/1759673.html