31 changes: 31 additions & 0 deletions libc/src/sys/mman/linux/posix_madvise.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===---------- Linux implementation of the POSIX posix_madvise function --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/sys/mman/posix_madvise.h"

#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/common.h"

#include <sys/syscall.h> // For syscall numbers.

namespace __llvm_libc {

// This function is currently linux only. It has to be refactored suitably if
// posix_madvise is to be supported on non-linux operating systems also.
LLVM_LIBC_FUNCTION(int, posix_madvise, (void *addr, size_t size, int advice)) {
// POSIX_MADV_DONTNEED does nothing because the default MADV_DONTNEED may
// cause data loss, which the posix madvise does not allow.
if (advice == POSIX_MADV_DONTNEED) {
return 0;
}
long ret_val = __llvm_libc::syscall(SYS_madvise, reinterpret_cast<long>(addr),
size, advice);
return ret_val < 0 ? -ret_val : 0;
}

} // namespace __llvm_libc
20 changes: 20 additions & 0 deletions libc/src/sys/mman/madvise.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for madvise function --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_SYS_MMAN_MADVISE_H
#define LLVM_LIBC_SRC_SYS_MMAN_MADVISE_H

#include <sys/mman.h> // For size_t and off_t

namespace __llvm_libc {

int madvise(void *addr, size_t size, int advice);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_SYS_MMAN_MADVISE_H
20 changes: 20 additions & 0 deletions libc/src/sys/mman/mprotect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for mprotect function -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_SYS_MMAN_MPROTECT_H
#define LLVM_LIBC_SRC_SYS_MMAN_MPROTECT_H

#include <sys/mman.h> // For size_t and off_t

namespace __llvm_libc {

int mprotect(void *addr, size_t size, int prot);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_SYS_MMAN_MPROTECT_H
20 changes: 20 additions & 0 deletions libc/src/sys/mman/posix_madvise.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for posix_madvise function --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_SYS_MMAN_POSIX_MADVISE_H
#define LLVM_LIBC_SRC_SYS_MMAN_POSIX_MADVISE_H

#include <sys/mman.h> // For size_t and off_t

namespace __llvm_libc {

int posix_madvise(void *addr, size_t size, int advice);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_SYS_MMAN_POSIX_MADVISE_H
50 changes: 50 additions & 0 deletions libc/test/src/sys/mman/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,53 @@ add_libc_unittest(
libc.src.sys.mman.munmap
libc.test.errno_setter_matcher
)

add_libc_unittest(
mprotect_test
SUITE
libc_sys_mman_unittests
SRCS
mprotect_test.cpp
DEPENDS
libc.include.errno
libc.include.sys_mman
libc.include.signal
libc.src.errno.errno
libc.src.sys.mman.mmap
libc.src.sys.mman.munmap
libc.src.sys.mman.mprotect
libc.test.errno_setter_matcher
)

add_libc_unittest(
madvise_test
SUITE
libc_sys_mman_unittests
SRCS
madvise_test.cpp
DEPENDS
libc.include.errno
libc.include.sys_mman
libc.src.errno.errno
libc.src.sys.mman.mmap
libc.src.sys.mman.munmap
libc.src.sys.mman.madvise
libc.test.errno_setter_matcher
)


add_libc_unittest(
posix_madvise_test
SUITE
libc_sys_mman_unittests
SRCS
posix_madvise_test.cpp
DEPENDS
libc.include.errno
libc.include.sys_mman
libc.src.errno.errno
libc.src.sys.mman.mmap
libc.src.sys.mman.munmap
libc.src.sys.mman.posix_madvise
libc.test.errno_setter_matcher
)
42 changes: 42 additions & 0 deletions libc/test/src/sys/mman/linux/madvise_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===-- Unittests for madvise ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/sys/mman/madvise.h"
#include "src/sys/mman/mmap.h"
#include "src/sys/mman/munmap.h"
#include "test/ErrnoSetterMatcher.h"
#include "utils/UnitTest/Test.h"

#include <errno.h>
#include <sys/mman.h>

using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;

TEST(LlvmLibcMadviseTest, NoError) {
size_t alloc_size = 128;
errno = 0;
void *addr = __llvm_libc::mmap(nullptr, alloc_size, PROT_READ,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
EXPECT_EQ(0, errno);
EXPECT_NE(addr, MAP_FAILED);

EXPECT_THAT(__llvm_libc::madvise(addr, alloc_size, MADV_RANDOM), Succeeds());

int *array = reinterpret_cast<int *>(addr);
// Reading from the memory should not crash the test.
// Since we used the MAP_ANONYMOUS flag, the contents of the newly
// allocated memory should be initialized to zero.
EXPECT_EQ(array[0], 0);
EXPECT_THAT(__llvm_libc::munmap(addr, alloc_size), Succeeds());
}

TEST(LlvmLibcMadviseTest, Error_BadPtr) {
errno = 0;
EXPECT_THAT(__llvm_libc::madvise(nullptr, 8, MADV_SEQUENTIAL), Fails(ENOMEM));
}
62 changes: 62 additions & 0 deletions libc/test/src/sys/mman/linux/mprotect_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===-- Unittests for mprotect --------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "include/signal.h"
#include "src/sys/mman/mmap.h"
#include "src/sys/mman/mprotect.h"
#include "src/sys/mman/munmap.h"
#include "test/ErrnoSetterMatcher.h"
#include "utils/UnitTest/Test.h"

#include <errno.h>
#include <sys/mman.h>

using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;

TEST(LlvmLibcMProtectTest, NoError) {
size_t alloc_size = 128;
errno = 0;
void *addr = __llvm_libc::mmap(nullptr, alloc_size, PROT_READ,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
EXPECT_EQ(0, errno);
EXPECT_NE(addr, MAP_FAILED);

int *array = reinterpret_cast<int *>(addr);
// Reading from the memory should not crash the test.
// Since we used the MAP_ANONYMOUS flag, the contents of the newly
// allocated memory should be initialized to zero.
EXPECT_EQ(array[0], 0);

// By setting the memory protection to read and write, we should be able to
// modify that memory.
EXPECT_THAT(__llvm_libc::mprotect(addr, alloc_size, PROT_READ | PROT_WRITE),
Succeeds());
array[0] = 1;
EXPECT_EQ(array[0], 1);

EXPECT_THAT(__llvm_libc::munmap(addr, alloc_size), Succeeds());
}

TEST(LlvmLibcMProtectTest, Error_InvalidWrite) {
// attempting to write to a read-only protected part of memory should cause a
// segfault.
EXPECT_DEATH(
[] {
size_t alloc_size = 128;
void *addr =
__llvm_libc::mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
__llvm_libc::mprotect(addr, alloc_size, PROT_READ);

(reinterpret_cast<char *>(addr))[0] = 'A';
},
WITH_SIGNAL(SIGSEGV));
// Reading from a write only segment may succeed on some platforms, so there's
// no test to check that.
}
49 changes: 49 additions & 0 deletions libc/test/src/sys/mman/linux/posix_madvise_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===-- Unittests for posix_madvise ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/sys/mman/mmap.h"
#include "src/sys/mman/munmap.h"
#include "src/sys/mman/posix_madvise.h"
#include "test/ErrnoSetterMatcher.h"
#include "utils/UnitTest/Test.h"

#include <errno.h>
#include <sys/mman.h>

using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;

TEST(LlvmLibcPosixMadviseTest, NoError) {
size_t alloc_size = 128;
errno = 0;
void *addr = __llvm_libc::mmap(nullptr, alloc_size, PROT_READ,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
EXPECT_EQ(0, errno);
EXPECT_NE(addr, MAP_FAILED);

EXPECT_EQ(__llvm_libc::posix_madvise(addr, alloc_size, POSIX_MADV_RANDOM), 0);

int *array = reinterpret_cast<int *>(addr);
// Reading from the memory should not crash the test.
// Since we used the MAP_ANONYMOUS flag, the contents of the newly
// allocated memory should be initialized to zero.
EXPECT_EQ(array[0], 0);
EXPECT_THAT(__llvm_libc::munmap(addr, alloc_size), Succeeds());
}

TEST(LlvmLibcPosixMadviseTest, Error_BadPtr) {
errno = 0;
// posix_madvise is a no-op on DONTNEED, so it shouldn't fail even with the
// nullptr.
EXPECT_EQ(__llvm_libc::posix_madvise(nullptr, 8, POSIX_MADV_DONTNEED), 0);

// posix_madvise doesn't set errno, but the return value is actually the error
// code.
EXPECT_EQ(__llvm_libc::posix_madvise(nullptr, 8, POSIX_MADV_SEQUENTIAL),
ENOMEM);
}