mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 00:45:18 +03:00
f9358e566a
Summary: This diff is auto-generated to upgrade the Pyre version and suppress errors in eden. The upgrade will affect Pyre local configurations in the following directories: ``` eden ``` Differential Revision: D21687853 fbshipit-source-id: baf0d9bc33f86da63ea289690faca6cf4d566588
114 lines
4.1 KiB
Python
114 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright (c) Facebook, Inc. and its affiliates.
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2.
|
|
|
|
import ctypes
|
|
import mmap
|
|
import os
|
|
import subprocess
|
|
from ctypes import c_int, c_size_t, c_ssize_t, c_void_p
|
|
|
|
from .lib import testcase
|
|
|
|
|
|
# python's mmap does not allow mapping larger than the file's size, so call
|
|
# it directly with ctypes.
|
|
#
|
|
# We have to ignore type errors on the next line until we update to a version
|
|
# of typeshed that includes https://github.com/python/typeshed/pull/3945
|
|
libc = ctypes.CDLL(None)
|
|
c_off_t = c_ssize_t
|
|
|
|
libc.mmap.argtypes = [c_void_p, c_size_t, c_int, c_int, c_off_t]
|
|
libc.mmap.restype = ctypes.POINTER(ctypes.c_byte)
|
|
|
|
libc.munmap.restype = c_void_p
|
|
libc.munmap.argtypes = [c_void_p, c_size_t]
|
|
|
|
|
|
@testcase.eden_repo_test
|
|
# pyre-ignore[13]: T62487924
|
|
class MmapTest(testcase.EdenRepoTest):
|
|
contents = "abcdef"
|
|
filename: str
|
|
|
|
def populate_repo(self) -> None:
|
|
self.repo.write_file("filename", self.contents)
|
|
self.repo.commit("Initial commit.")
|
|
self.filename = os.path.join(self.mount, "filename")
|
|
|
|
def test_mmap_in_backed_file_is_null_terminated(self) -> None:
|
|
fd = os.open(self.filename, os.O_RDONLY)
|
|
try:
|
|
size = os.fstat(fd).st_size
|
|
self.assertEqual(len(self.contents), size)
|
|
|
|
map_size = (size + 4095) // 4096 * 4096
|
|
self.assertNotEqual(size, map_size)
|
|
|
|
m = libc.mmap(None, map_size, mmap.PROT_READ, mmap.MAP_PRIVATE, fd, 0)
|
|
try:
|
|
# assert the additional mapped bytes are null, per `man 2 mmap`
|
|
for i in range(size, map_size):
|
|
self.assertEqual(0, m[i])
|
|
finally:
|
|
libc.munmap(m, map_size)
|
|
finally:
|
|
os.close(fd)
|
|
|
|
def test_mmap_is_null_terminated_after_truncate_and_write_to_overlay(self) -> None:
|
|
# WARNING: This test is very fiddly.
|
|
|
|
# The bug is that if a file in Eden is opened with O_TRUNC followed by
|
|
# a series of writes, then mmap of that file with a size larger than the
|
|
# file (but still within the trailing page) does not zero the trailing
|
|
# bytes. Clang relies on this mmap behavior to enforce that the buffer
|
|
# is null-terminated. Since the buffer ends up not being null-
|
|
# terminated, Clang segfaults.
|
|
#
|
|
# It seems like this is a kernel or FUSE bug more than an Eden bug,
|
|
# but we should verify nonetheless that it does not occur.
|
|
|
|
# If this test uses the same file committed in populate_repo, the bug
|
|
# does not reproduce.
|
|
filename = os.path.join(self.mount, "filename2")
|
|
|
|
# Write to the file from another process. if this process writes the
|
|
# file, the bug is not reproduced.
|
|
subprocess.check_call(
|
|
["dd", "if=/dev/urandom", "of=" + filename, "bs=4096", "count=6"]
|
|
)
|
|
|
|
# A few pages, with data slightly beyond a page boundary.
|
|
new_contents = b"abcd" * 3072 + b"abcdef"
|
|
new_size = len(new_contents)
|
|
|
|
# Write to the file with another process. if this process writes the
|
|
# file, the bug is not reproduced.
|
|
with subprocess.Popen(
|
|
["dd", "of=" + filename, "bs=512"], stdin=subprocess.PIPE
|
|
) as p:
|
|
# pyre-fixme[16]: Optional type has no attribute `write`.
|
|
p.stdin.write(new_contents)
|
|
|
|
fd = os.open(filename, os.O_RDONLY)
|
|
try:
|
|
size = os.fstat(fd).st_size
|
|
self.assertEqual(new_size, size)
|
|
|
|
# Map all the way up to a page boundary.
|
|
map_size = (size + 4095) // 4096 * 4096
|
|
self.assertNotEqual(size, map_size)
|
|
|
|
m = libc.mmap(None, map_size, mmap.PROT_READ, mmap.MAP_PRIVATE, fd, 0)
|
|
try:
|
|
# Assert the additional mapped bytes are null, per `man 2 mmap`
|
|
for i in range(size, map_size):
|
|
self.assertEqual(0, m[i])
|
|
finally:
|
|
libc.munmap(m, map_size)
|
|
finally:
|
|
os.close(fd)
|