Fix brk/sbrk error checking.
Note that the kernel returns the current break on error or if the requested
break is smaller than the minimum break, or the new break. I don't know where
we got the idea that the kernel could return -1.
Also optimizes the query case.
Also hides an accidentally-exported symbol for LP64.
Change-Id: I0fd6b8b14ddf1ae82935c0c3fc610da5cc74932e
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 70b23bd..f56b767 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -18,6 +18,8 @@
#include "ScopedSignalHandler.h"
#include "TemporaryFile.h"
+#define __STDC_LIMIT_MACROS // For glibc.
+
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
@@ -29,14 +31,49 @@
ASSERT_GT(sysconf(_SC_MONOTONIC_CLOCK), 0);
}
-TEST(unistd, sbrk) {
- void* initial_break = sbrk(0);
+static void* get_brk() {
+ return sbrk(0);
+}
- void* new_break = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(initial_break) + 2000);
+static void* page_align(uintptr_t addr) {
+ uintptr_t mask = sysconf(_SC_PAGE_SIZE) - 1;
+ return reinterpret_cast<void*>((addr + mask) & ~mask);
+}
+
+TEST(unistd, brk) {
+ void* initial_break = get_brk();
+
+ // The kernel aligns the break to a page.
+ void* new_break = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(initial_break) + 1);
ASSERT_EQ(0, brk(new_break));
+ ASSERT_GE(get_brk(), new_break);
- void* final_break = sbrk(0);
- ASSERT_EQ(final_break, new_break);
+ new_break = page_align(reinterpret_cast<uintptr_t>(initial_break) + sysconf(_SC_PAGE_SIZE));
+ ASSERT_EQ(0, brk(new_break));
+ ASSERT_EQ(get_brk(), new_break);
+}
+
+TEST(unistd, brk_ENOMEM) {
+ ASSERT_EQ(-1, brk(reinterpret_cast<void*>(-1)));
+ ASSERT_EQ(ENOMEM, errno);
+}
+
+TEST(unistd, sbrk_ENOMEM) {
+ intptr_t current_brk = reinterpret_cast<intptr_t>(get_brk());
+
+ // Can't increase by so much that we'd overflow.
+ ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(PTRDIFF_MAX));
+ ASSERT_EQ(ENOMEM, errno);
+
+ // Can't reduce by more than the current break.
+ ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(-(current_brk + 1)));
+ ASSERT_EQ(ENOMEM, errno);
+
+#if !defined(__GLIBC__)
+ // The maximum negative value is an interesting special case that glibc gets wrong.
+ ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(PTRDIFF_MIN));
+ ASSERT_EQ(ENOMEM, errno);
+#endif
}
TEST(unistd, truncate) {