Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions base/allocator/dispatcher/tls.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include "base/immediate_crash.h"
#include "build/build_config.h"

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD))
#include <sys/prctl.h>
#endif

Expand Down Expand Up @@ -51,7 +51,7 @@ void Swap(std::atomic_bool& lh_op, std::atomic_bool& rh_op) {
void* MMapAllocator::AllocateMemory(size_t size_in_bytes) {
void* const mmap_res = mmap(nullptr, size_in_bytes, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD))
#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
if (mmap_res != MAP_FAILED) {
// Allow the anonymous memory region allocated by mmap(MAP_ANONYMOUS) to
Expand Down
4 changes: 2 additions & 2 deletions base/memory/madv_free_discardable_memory_posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#include "base/tracing_buildflags.h"
#include "build/build_config.h"

#if BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_STARBOARD)
#include <sys/prctl.h>
#endif

Expand All @@ -49,7 +49,7 @@ void* AllocatePages(size_t size_in_pages) {
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
PCHECK(data != MAP_FAILED);

#if BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_STARBOARD)
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, data, length,
"madv-free-discardable");
#endif
Expand Down
4 changes: 2 additions & 2 deletions base/metrics/persistent_memory_allocator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#include "base/win/winbase_shim.h"
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
#include <sys/mman.h>
#if BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_STARBOARD)
#include <sys/prctl.h>
#endif
#endif
Expand Down Expand Up @@ -1097,7 +1097,7 @@ LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size,
address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED,
-1, 0);
if (address != MAP_FAILED) {
#if BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_STARBOARD)
// Allow the anonymous memory region allocated by mmap(MAP_ANON) to be
// identified in /proc/$PID/smaps. This helps improve visibility into
// Chrome's memory usage on Android.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#include <Security/Security.h>
#include <mach/mach.h>
#endif
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD))
#include <sys/prctl.h>
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
Expand All @@ -62,7 +62,7 @@ namespace partition_alloc::internal {

namespace {

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD))
#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
const char* PageTagToName(PageTag tag) {
// Important: All the names should be string literals. As per prctl.h in
Expand All @@ -86,7 +86,7 @@ const char* PageTagToName(PageTag tag) {
}
}
#endif
#endif // BUILDFLAG(IS_ANDROID)
#endif // BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD))

#if BUILDFLAG(IS_MAC)
// Tests whether the version of macOS supports the MAP_JIT flag and if the
Expand Down Expand Up @@ -197,7 +197,7 @@ uintptr_t SystemAllocPagesInternal(uintptr_t hint,
ret = nullptr;
}

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD))
#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
// On Android and Linux, anonymous mappings can have a name attached to them.
// This is useful for debugging, and double-checking memory attribution.
Expand Down
1 change: 1 addition & 0 deletions starboard/elf_loader/exported_symbols.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ ExportedSymbols::ExportedSymbols() {

// POSIX APIs
REGISTER_SYMBOL(aligned_alloc);
REGISTER_SYMBOL(atexit);
REGISTER_SYMBOL(calloc);
REGISTER_SYMBOL(close);
REGISTER_SYMBOL(fdatasync);
Expand Down
1 change: 1 addition & 0 deletions starboard/nplb/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ test("nplb") {
"posix_compliance/posix_poll_test.cc",
"posix_compliance/posix_posix_memory_allocate_aligned_test.cc",
"posix_compliance/posix_prctl_test.cc",
"posix_compliance/posix_prctl_variadic_test.cc",
"posix_compliance/posix_priority_test.cc",
"posix_compliance/posix_process_test.cc",
"posix_compliance/posix_rand_r_test.cc",
Expand Down
252 changes: 252 additions & 0 deletions starboard/nplb/posix_compliance/posix_prctl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,123 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/types.h>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Include <sys/mman.h> to provide definitions for mmap and munmap, which are better suited for testing PR_SET_VMA than malloc.

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

#include <unistd.h>

#include <algorithm>
#include <mutex>

#include "starboard/common/log.h"
#include "starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.h"
#include "testing/gtest/include/gtest/gtest.h"

// From third_party/musl/include/sys/prctl.h
#ifndef PR_SET_VMA
#define PR_SET_VMA 0x53564d41
#endif
#ifndef PR_SET_VMA_ANON_NAME
#define PR_SET_VMA_ANON_NAME 0
#endif

namespace nplb {
namespace {

// --- Fallback Mechanism for Testing ---
const char kVmaTagsFileNamePrefix[] = "cobalt_vma_tags";
std::mutex g_vma_tags_mutex;
char g_vma_tags_file_path[256] = {0};

void VmaTagFileCleanup() {
std::lock_guard<std::mutex> lock(g_vma_tags_mutex);
if (g_vma_tags_file_path[0] != '\0') {
unlink(g_vma_tags_file_path);
g_vma_tags_file_path[0] = '\0';
}
}

const char* GetTempDir() {
const char* tmpdir = getenv("TMPDIR");
if (!tmpdir) {
tmpdir = "/tmp";
}
return tmpdir;
}

// Intercept prctl for SetVmaAnonName test to provide fallback.
int test_prctl_wrapper(int option, ...) {
unsigned long arg2 = 0;
unsigned long arg3 = 0;
unsigned long arg4 = 0;
unsigned long arg5 = 0;

va_list args;
va_start(args, option);
arg2 = va_arg(args, unsigned long);
if (option == PR_SET_VMA) {
arg3 = va_arg(args, unsigned long);
arg4 = va_arg(args, unsigned long);
arg5 = va_arg(args, unsigned long);
}
va_end(args);

int result = prctl(option, arg2, arg3, arg4, arg5);

if (result == -1 && option == PR_SET_VMA && arg2 == PR_SET_VMA_ANON_NAME) {
int saved_errno = errno;
std::lock_guard<std::mutex> lock(g_vma_tags_mutex);
int fd = -1;

if (g_vma_tags_file_path[0] != '\0') {
fd = open(g_vma_tags_file_path, O_WRONLY | O_APPEND | O_CLOEXEC);
if (fd == -1) {
g_vma_tags_file_path[0] = '\0';
}
}

if (fd == -1 && g_vma_tags_file_path[0] == '\0') {
char path_template[256];
snprintf(path_template, sizeof(path_template), "%s/%s_XXXXXX",
GetTempDir(), kVmaTagsFileNamePrefix);
fd = mkstemp(path_template);
if (fd != -1) {
snprintf(g_vma_tags_file_path, sizeof(g_vma_tags_file_path), "%s",
path_template);
static bool cleanup_registered = false;
if (!cleanup_registered) {
atexit(VmaTagFileCleanup);
cleanup_registered = true;
}
}
}

if (fd != -1) {
char buf[256];
int len = snprintf(buf, sizeof(buf), "0x%lx 0x%lx %s\n", arg3,
arg3 + arg4, reinterpret_cast<const char*>(arg5));
if (len > 0) {
size_t write_len = std::min(static_cast<size_t>(len), sizeof(buf) - 1);
if (write(fd, buf, write_len) != -1) {
close(fd);
errno = saved_errno;
return 0;
}
}
close(fd);
}
errno = saved_errno;
}
return result;
}

TEST(PosixPrctlGeneralTests, FailsWithInvalidOption) {
errno = 0;
EXPECT_EQ(-1, prctl(-1, 0, 0, 0, 0));
Expand Down Expand Up @@ -527,5 +632,152 @@ TEST_F(PosixPrctlPtracerTests, SetFailsWithInvalidValue) {
<< errno << " (" << strerror(errno) << ")";
}

const char kVmaName[] = "TestVmaName";

// Checks /proc/self/maps to see if the memory mapping containing |addr| has
// the specified |name|. This is the expected outcome when the kernel supports
// PR_SET_VMA_ANON_NAME.
bool VmaIsNamed(void* addr, const char* name) {
FILE* fp = fopen("/proc/self/maps", "r");
if (!fp) {
// If we can't open /proc/self/maps, we can't verify.
// Assume it's not named and let the fallback check proceed.
return false;
}

char line[1024];
bool found = false;
unsigned long target_addr = reinterpret_cast<unsigned long>(addr);

while (fgets(line, sizeof(line), fp)) {
unsigned long start, end;
if (sscanf(line, "%lx-%lx", &start, &end) != 2) {
continue;
}
if (target_addr >= start && target_addr < end) {
if (strstr(line, name)) {
found = true;
}
// We found the mapping containing our address, so we can stop.
// If it's not named here, it's not named.
break;
}
}

fclose(fp);
return found;
}

// Checks for the existence and content of any fallback file matching the
// prefix. This is the expected outcome when the kernel does NOT support
// PR_SET_VMA_ANON_NAME.
bool FallbackFileIsCorrect(unsigned long start,
unsigned long size,
const char* name) {
const char* tmpdir = GetTempDir();
DIR* dir = opendir(tmpdir);
if (!dir) {
return false;
}

bool found = false;
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
if (strncmp(entry->d_name, kVmaTagsFileNamePrefix,
strlen(kVmaTagsFileNamePrefix)) == 0) {
char file_path[512];
snprintf(file_path, sizeof(file_path), "%s/%s", tmpdir, entry->d_name);

FILE* fp = fopen(file_path, "r");
if (!fp) {
continue;
}

char line[1024];
while (fgets(line, sizeof(line), fp)) {
unsigned long file_start, file_end;
char file_name[256];
// The format is "0x%lx 0x%lx %s\n"
if (sscanf(line, "0x%lx 0x%lx %s", &file_start, &file_end, file_name) ==
3) {
if (file_start == start && file_end == start + size &&
strcmp(file_name, name) == 0) {
found = true;
break;
}
}
}
fclose(fp);
if (found) {
break;
}
}
}
closedir(dir);
return found;
}

// Helper to clean up any fallback files before and after the test.
void CleanupFallbackFiles() {
const char* tmpdir = GetTempDir();
DIR* dir = opendir(tmpdir);
if (!dir) {
return;
}

struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
if (strncmp(entry->d_name, kVmaTagsFileNamePrefix,
strlen(kVmaTagsFileNamePrefix)) == 0) {
char file_path[512];
snprintf(file_path, sizeof(file_path), "%s/%s", tmpdir, entry->d_name);
unlink(file_path);
}
}
closedir(dir);
}

// This test verifies that prctl with PR_SET_VMA and PR_SET_VMA_ANON_NAME
// correctly names a VMA region, either by calling the underlying prctl syscall
// or by using the fallback mechanism of writing to a file.
TEST(PosixPrctlTest, SetVmaAnonName) {
const size_t kMapSize = 4096;
void* p = malloc(kMapSize);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

PR_SET_VMA is designed to work with anonymous memory mappings. Memory from malloc might not be a distinct anonymous mapping (e.g., it could be part of the heap's brk segment), which can lead to prctl returning EINVAL. Using mmap ensures a valid target for the test.

  void* p = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  ASSERT_NE(p, MAP_FAILED);

ASSERT_NE(p, nullptr);

// Ensure we have a clean state by deleting any leftover fallback files.
CleanupFallbackFiles();

int result = test_prctl_wrapper(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
reinterpret_cast<unsigned long>(p), kMapSize,
reinterpret_cast<unsigned long>(kVmaName));

// The wrapper should return 0 on success, for both the prctl call and the
// fallback.
EXPECT_EQ(result, 0);

bool vma_is_named = VmaIsNamed(p, kVmaName);
bool fallback_file_is_correct = FallbackFileIsCorrect(
reinterpret_cast<unsigned long>(p), kMapSize, kVmaName);

// We expect one of the two mechanisms to have worked.
EXPECT_TRUE(vma_is_named || fallback_file_is_correct)
<< "VMA name was not set via prctl and fallback file was not created or "
"is incorrect.";

if (vma_is_named) {
SB_LOG(INFO) << "VMA naming with PR_SET_VMA_ANON_NAME is supported by the "
"kernel.";
}
if (fallback_file_is_correct) {
SB_LOG(INFO) << "VMA naming with PR_SET_VMA_ANON_NAME is NOT supported by "
"the kernel, fallback was used.";
}

free(p);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use munmap to release memory allocated with mmap.

  munmap(p, kMapSize);

// Clean up the fallback files created during the test.
CleanupFallbackFiles();
}

} // namespace
} // namespace nplb
Loading
Loading