Skip to content

Commit b457cb7

Browse files
enh-googleandi34
authored andcommitted
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. Bug: 28740702 (cherry picked from commit 533dde4) Change-Id: Ied16987756a501acf292368a14e3727ad631efa5
1 parent 081db84 commit b457cb7

File tree

4 files changed

+86
-66
lines changed

4 files changed

+86
-66
lines changed

libc/Android.mk

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ libc_bionic_src_files := \
228228
bionic/pthread_setschedparam.cpp \
229229
bionic/pthread_sigmask.cpp \
230230
bionic/raise.cpp \
231-
bionic/sbrk.cpp \
232231
bionic/scandir.cpp \
233232
bionic/sched_getaffinity.cpp \
234233
bionic/__set_errno.cpp \

libc/bionic/brk.cpp

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,52 @@
2626
* SUCH DAMAGE.
2727
*/
2828

29+
#define __STDINT_LIMITS
30+
31+
#include <errno.h>
32+
#include <stdint.h>
2933
#include <unistd.h>
3034

31-
/* Shared with sbrk.c. */
32-
extern "C" void* __bionic_brk; // TODO: should be __LIBC_HIDDEN__ but accidentally exported by NDK :-(
35+
#if __LP64__
36+
static void* __bionic_brk;
37+
#else
38+
void* __bionic_brk; // Accidentally exported by the NDK.
39+
#endif
3340

3441
int brk(void* end_data) {
35-
void* new_brk = __brk(end_data);
36-
if (new_brk != end_data) {
42+
__bionic_brk = __brk(end_data);
43+
if (__bionic_brk < end_data) {
44+
errno = ENOMEM;
3745
return -1;
3846
}
39-
__bionic_brk = new_brk;
4047
return 0;
4148
}
49+
50+
void* sbrk(ptrdiff_t increment) {
51+
// Initialize __bionic_brk if necessary.
52+
if (__bionic_brk == NULL) {
53+
__bionic_brk = __brk(NULL);
54+
}
55+
56+
// Don't ask the kernel if we already know the answer.
57+
if (increment == 0) {
58+
return __bionic_brk;
59+
}
60+
61+
// Avoid overflow.
62+
intptr_t old_brk = reinterpret_cast<intptr_t>(__bionic_brk);
63+
if ((increment > 0 && INTPTR_MAX - increment > old_brk) ||
64+
(increment < 0 && (increment == PTRDIFF_MIN || old_brk < -increment))) {
65+
errno = ENOMEM;
66+
return reinterpret_cast<void*>(-1);
67+
}
68+
69+
void* desired_brk = reinterpret_cast<void*>(old_brk + increment);
70+
__bionic_brk = __brk(desired_brk);
71+
72+
if (__bionic_brk < desired_brk) {
73+
errno = ENOMEM;
74+
return reinterpret_cast<void*>(-1);
75+
}
76+
return reinterpret_cast<void*>(old_brk);
77+
}

libc/bionic/sbrk.cpp

Lines changed: 0 additions & 55 deletions
This file was deleted.

tests/unistd_test.cpp

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,61 @@
1414
* limitations under the License.
1515
*/
1616

17+
#define __STDINT_LIMITS
18+
1719
#include <gtest/gtest.h>
1820

21+
#include <errno.h>
1922
#include <stdint.h>
2023
#include <unistd.h>
2124

2225
TEST(unistd, sysconf_SC_MONOTONIC_CLOCK) {
2326
ASSERT_GT(sysconf(_SC_MONOTONIC_CLOCK), 0);
2427
}
2528

26-
TEST(unistd, sbrk) {
27-
void* initial_break = sbrk(0);
29+
static void* get_brk() {
30+
return sbrk(0);
31+
}
32+
33+
static void* page_align(uintptr_t addr) {
34+
uintptr_t mask = sysconf(_SC_PAGE_SIZE) - 1;
35+
return reinterpret_cast<void*>((addr + mask) & ~mask);
36+
}
37+
38+
TEST(unistd, brk) {
39+
void* initial_break = get_brk();
40+
41+
// The kernel aligns the break to a page.
42+
void* new_break = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(initial_break) + 1);
43+
ASSERT_EQ(0, brk(new_break));
44+
ASSERT_GE(get_brk(), new_break);
2845

29-
void* new_break = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(initial_break) + 2000);
46+
new_break = page_align(reinterpret_cast<uintptr_t>(initial_break) + sysconf(_SC_PAGE_SIZE));
3047
ASSERT_EQ(0, brk(new_break));
48+
ASSERT_EQ(get_brk(), new_break);
49+
}
50+
51+
TEST(unistd, brk_ENOMEM) {
52+
ASSERT_EQ(-1, brk(reinterpret_cast<void*>(-1)));
53+
ASSERT_EQ(ENOMEM, errno);
54+
}
55+
56+
TEST(unistd, sbrk_ENOMEM) {
57+
intptr_t current_brk = reinterpret_cast<intptr_t>(get_brk());
58+
59+
#if !defined(__GLIBC__)
60+
// Can't increase by so much that we'd overflow.
61+
ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(PTRDIFF_MAX));
62+
ASSERT_EQ(ENOMEM, errno);
63+
#endif
64+
65+
// Can't reduce by more than the current break.
66+
ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(-(current_brk + 1)));
67+
ASSERT_EQ(ENOMEM, errno);
3168

32-
void* final_break = sbrk(0);
33-
ASSERT_EQ(final_break, new_break);
69+
#if !defined(__GLIBC__)
70+
// The maximum negative value is an interesting special case that glibc gets wrong.
71+
ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(PTRDIFF_MIN));
72+
ASSERT_EQ(ENOMEM, errno);
73+
#endif
3474
}

0 commit comments

Comments
 (0)