Merge "Add %b and %B support to the scanf/wscanf and strto*/wcsto* families."
diff --git a/libc/bionic/heap_tagging.h b/libc/bionic/heap_tagging.h
index a3588b8..5bc1da0 100644
--- a/libc/bionic/heap_tagging.h
+++ b/libc/bionic/heap_tagging.h
@@ -43,3 +43,19 @@
// This function can be called in a multithreaded context, and thus should
// only be called when holding the `g_heap_tagging_lock`.
bool SetHeapTaggingLevel(HeapTaggingLevel level);
+
+// This is static because libc_nomalloc uses this but does not need to link the
+// cpp file.
+__attribute__((unused)) static inline const char* DescribeTaggingLevel(
+ HeapTaggingLevel level) {
+ switch (level) {
+ case M_HEAP_TAGGING_LEVEL_NONE:
+ return "none";
+ case M_HEAP_TAGGING_LEVEL_TBI:
+ return "tbi";
+ case M_HEAP_TAGGING_LEVEL_ASYNC:
+ return "async";
+ case M_HEAP_TAGGING_LEVEL_SYNC:
+ return "sync";
+ }
+}
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 8084e73..5d5ecac 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -27,11 +27,12 @@
*/
#include "libc_init_common.h"
-#include "heap_tagging.h"
+#include <async_safe/log.h>
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -42,8 +43,8 @@
#include <sys/time.h>
#include <unistd.h>
-#include <async_safe/log.h>
-
+#include "heap_tagging.h"
+#include "private/ScopedPthreadMutexLocker.h"
#include "private/WriteProtected.h"
#include "private/bionic_defs.h"
#include "private/bionic_globals.h"
@@ -104,6 +105,51 @@
}
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
+__attribute__((no_sanitize("hwaddress", "memtag"))) void
+__libc_init_mte_late() {
+#if defined(__aarch64__)
+ if (!__libc_shared_globals()->heap_tagging_upgrade_timer_sec) {
+ return;
+ }
+ struct sigevent event = {};
+ static timer_t timer;
+ event.sigev_notify = SIGEV_THREAD;
+ event.sigev_notify_function = [](union sigval) {
+ async_safe_format_log(ANDROID_LOG_INFO, "libc",
+ "Downgrading MTE to async.");
+ ScopedPthreadMutexLocker l(&g_heap_tagging_lock);
+ SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC);
+ timer_delete(timer);
+ };
+
+ if (timer_create(CLOCK_REALTIME, &event, &timer) == -1) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "Failed to create MTE downgrade timer: %m");
+ // Revert back to ASYNC. If we fail to create or arm the timer, otherwise
+ // the process would be indefinitely stuck in SYNC.
+ SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC);
+ return;
+ }
+
+ struct itimerspec timerspec = {};
+ timerspec.it_value.tv_sec =
+ __libc_shared_globals()->heap_tagging_upgrade_timer_sec;
+ if (timer_settime(timer, /* flags= */ 0, &timerspec, nullptr) == -1) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "Failed to arm MTE downgrade timer: %m");
+ // Revert back to ASYNC. If we fail to create or arm the timer, otherwise
+ // the process would be indefinitely stuck in SYNC.
+ SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC);
+ timer_delete(timer);
+ return;
+ }
+ async_safe_format_log(
+ ANDROID_LOG_INFO, "libc", "Armed MTE downgrade timer for %" PRId64 " s",
+ __libc_shared_globals()->heap_tagging_upgrade_timer_sec);
+#endif
+}
+
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
void __libc_add_main_thread() {
// Get the main thread from TLS and add it to the thread list.
pthread_internal_t* main_thread = __get_thread();
diff --git a/libc/bionic/libc_init_common.h b/libc/bionic/libc_init_common.h
index 15c747e..6b39d6d 100644
--- a/libc/bionic/libc_init_common.h
+++ b/libc/bionic/libc_init_common.h
@@ -60,6 +60,8 @@
__LIBC_HIDDEN__ void __libc_init_scudo();
+__LIBC_HIDDEN__ void __libc_init_mte_late();
+
__LIBC_HIDDEN__ void __libc_init_AT_SECURE(char** envp);
// The fork handler must be initialised after __libc_init_malloc, as
diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index 24efbf5..c61810e 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -154,6 +154,8 @@
__cxa_atexit(__libc_fini,structors->fini_array,nullptr);
}
+ __libc_init_mte_late();
+
exit(slingshot(args.argc - __libc_shared_globals()->initial_linker_arg_count,
args.argv + __libc_shared_globals()->initial_linker_arg_count,
args.envp));
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 79a4019..46eafc6 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -29,6 +29,7 @@
#include <android/api-level.h>
#include <elf.h>
#include <errno.h>
+#include <malloc.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -36,10 +37,9 @@
#include <sys/auxv.h>
#include <sys/mman.h>
+#include "async_safe/log.h"
+#include "heap_tagging.h"
#include "libc_init_common.h"
-#include "pthread_internal.h"
-#include "sysprop_helpers.h"
-
#include "platform/bionic/macros.h"
#include "platform/bionic/mte.h"
#include "platform/bionic/page.h"
@@ -51,7 +51,9 @@
#include "private/bionic_elf_tls.h"
#include "private/bionic_globals.h"
#include "private/bionic_tls.h"
+#include "pthread_internal.h"
#include "sys/system_properties.h"
+#include "sysprop_helpers.h"
#if __has_feature(hwaddress_sanitizer)
#include <sanitizer/hwasan_interface.h>
@@ -302,7 +304,34 @@
void* stack_top) {
bool memtag_stack;
HeapTaggingLevel level = __get_heap_tagging_level(phdr_start, phdr_ct, load_bias, &memtag_stack);
-
+ char* env = getenv("BIONIC_MEMTAG_UPGRADE_SECS");
+ int64_t timed_upgrade = 0;
+ if (env) {
+ char* endptr;
+ timed_upgrade = strtoll(env, &endptr, 10);
+ if (*endptr != '\0' || timed_upgrade < 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "Invalid value for BIONIC_MEMTAG_UPGRADE_SECS: %s",
+ env);
+ timed_upgrade = 0;
+ }
+ // Make sure that this does not get passed to potential processes inheriting
+ // this environment.
+ unsetenv("BIONIC_MEMTAG_UPGRADE_SECS");
+ }
+ if (timed_upgrade) {
+ if (level == M_HEAP_TAGGING_LEVEL_ASYNC) {
+ async_safe_format_log(ANDROID_LOG_INFO, "libc",
+ "Attempting timed MTE upgrade from async to sync.");
+ __libc_shared_globals()->heap_tagging_upgrade_timer_sec = timed_upgrade;
+ level = M_HEAP_TAGGING_LEVEL_SYNC;
+ } else if (level != M_HEAP_TAGGING_LEVEL_SYNC) {
+ async_safe_format_log(
+ ANDROID_LOG_ERROR, "libc",
+ "Requested timed MTE upgrade from invalid %s to sync. Ignoring.",
+ DescribeTaggingLevel(level));
+ }
+ }
if (level == M_HEAP_TAGGING_LEVEL_SYNC || level == M_HEAP_TAGGING_LEVEL_ASYNC) {
unsigned long prctl_arg = PR_TAGGED_ADDR_ENABLE | PR_MTE_TAG_SET_NONZERO;
prctl_arg |= (level == M_HEAP_TAGGING_LEVEL_SYNC) ? PR_MTE_TCF_SYNC : PR_MTE_TCF_ASYNC;
@@ -333,6 +362,8 @@
if (prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) == 0) {
__libc_shared_globals()->initial_heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
}
+ // We did not enable MTE, so we do not need to arm the upgrade timer.
+ __libc_shared_globals()->heap_tagging_upgrade_timer_sec = 0;
}
#else // __aarch64__
void __libc_init_mte(const void*, size_t, uintptr_t, void*) {}
@@ -384,6 +415,8 @@
__cxa_atexit(__libc_fini,structors->fini_array,nullptr);
}
+ __libc_init_mte_late();
+
exit(slingshot(args.argc, args.argv, args.envp));
}
diff --git a/libc/kernel/tools/cpp.py b/libc/kernel/tools/cpp.py
index 26121ca..1496231 100755
--- a/libc/kernel/tools/cpp.py
+++ b/libc/kernel/tools/cpp.py
@@ -155,6 +155,11 @@
pass
+class UnparseableStruct(Exception):
+ """An exception that will be raised for structs that cannot be parsed."""
+ pass
+
+
# The __contains__ function in libclang SourceRange class contains a bug. It
# gives wrong result when dealing with single line range.
# Bug filed with upstream:
@@ -1197,22 +1202,38 @@
def removeStructs(self, structs):
"""Remove structs."""
- for b in self.blocks:
+ extra_includes = []
+ block_num = 0
+ num_blocks = len(self.blocks)
+ while block_num < num_blocks:
+ b = self.blocks[block_num]
+ block_num += 1
# Have to look in each block for a top-level struct definition.
if b.directive:
continue
num_tokens = len(b.tokens)
- # A struct definition has at least 5 tokens:
+ # A struct definition usually looks like:
# struct
# ident
# {
# }
# ;
- if num_tokens < 5:
+ # However, the structure might be spread across multiple blocks
+ # if the structure looks like this:
+ # struct ident
+ # {
+ # #ifdef VARIABLE
+ # pid_t pid;
+ # #endif
+ # }:
+ # So the total number of tokens in the block might be less than
+ # five but assume at least three.
+ if num_tokens < 3:
continue
+
# This is a simple struct finder, it might fail if a top-level
# structure has an #if type directives that confuses the algorithm
- # for finding th end of the structure. Or if there is another
+ # for finding the end of the structure. Or if there is another
# structure definition embedded in the structure.
i = 0
while i < num_tokens - 2:
@@ -1223,24 +1244,58 @@
if (b.tokens[i + 1].kind == TokenKind.IDENTIFIER and
b.tokens[i + 2].kind == TokenKind.PUNCTUATION and
b.tokens[i + 2].id == "{" and b.tokens[i + 1].id in structs):
+ # Add an include for the structure to be removed of the form:
+ # #include <bits/STRUCT_NAME.h>
+ struct_token = b.tokens[i + 1]
+ if not structs[struct_token.id]:
+ extra_includes.append("<bits/%s.h>" % struct_token.id)
+
# Search forward for the end of the structure.
- # Very simple search, look for } and ; tokens. If something
- # more complicated is needed we can add it later.
+ # Very simple search, look for } and ; tokens.
+ # If we hit the end of the block, we'll need to start
+ # looking at the next block.
j = i + 3
- while j < num_tokens - 1:
- if (b.tokens[j].kind == TokenKind.PUNCTUATION and
- b.tokens[j].id == "}" and
- b.tokens[j + 1].kind == TokenKind.PUNCTUATION and
- b.tokens[j + 1].id == ";"):
- b.tokens = b.tokens[0:i] + b.tokens[j + 2:num_tokens]
+ depth = 1
+ struct_removed = False
+ while not struct_removed:
+ while j < num_tokens:
+ if b.tokens[j].kind == TokenKind.PUNCTUATION:
+ if b.tokens[j].id == '{':
+ depth += 1
+ elif b.tokens[j].id == '}':
+ depth -= 1
+ elif b.tokens[j].id == ';' and depth == 0:
+ b.tokens = b.tokens[0:i] + b.tokens[j + 1:num_tokens]
+ num_tokens = len(b.tokens)
+ struct_removed = True
+ break
+ j += 1
+ if not struct_removed:
+ b.tokens = b.tokens[0:i]
+
+ # Skip directive blocks.
+ start_block = block_num
+ while block_num < num_blocks:
+ if not self.blocks[block_num].directive:
+ break
+ block_num += 1
+ if block_num >= num_blocks:
+ # Unparsable struct, error out.
+ raise UnparseableStruct("Cannot remove struct %s: %s" % (struct_token.id, struct_token.location))
+ self.blocks = self.blocks[0:start_block] + self.blocks[block_num:num_blocks]
+ num_blocks = len(self.blocks)
+ b = self.blocks[start_block]
+ block_num = start_block + 1
num_tokens = len(b.tokens)
- j = i
- break
- j += 1
- i = j
+ i = 0
+ j = 0
continue
i += 1
+ for extra_include in extra_includes:
+ replacement = CppStringTokenizer(extra_include)
+ self.blocks.insert(2, Block(replacement.tokens, directive='include'))
+
def optimizeAll(self, macros):
self.optimizeMacros(macros)
self.optimizeIf01()
@@ -1404,35 +1459,12 @@
def replaceTokens(self, replacements):
"""Replace tokens according to the given dict."""
- extra_includes = []
for b in self.blocks:
made_change = False
if b.isInclude() is None:
i = 0
while i < len(b.tokens):
tok = b.tokens[i]
- if (tok.kind == TokenKind.KEYWORD and tok.id == 'struct'
- and (i + 2) < len(b.tokens) and b.tokens[i + 2].id == '{'):
- struct_name = b.tokens[i + 1].id
- if struct_name in kernel_struct_replacements:
- extra_includes.append("<bits/%s.h>" % struct_name)
- end = i + 2
- depth = 1
- while end < len(b.tokens) and depth > 0:
- if b.tokens[end].id == '}':
- depth -= 1
- elif b.tokens[end].id == '{':
- depth += 1
- end += 1
- end += 1 # Swallow last '}'
- while end < len(b.tokens) and b.tokens[end].id != ';':
- end += 1
- end += 1 # Swallow ';'
- # Remove these tokens. We'll replace them later with a #include block.
- b.tokens[i:end] = []
- made_change = True
- # We've just modified b.tokens, so revisit the current offset.
- continue
if tok.kind == TokenKind.IDENTIFIER:
if tok.id in replacements:
tok.id = replacements[tok.id]
@@ -1447,10 +1479,6 @@
# Keep 'expr' in sync with 'tokens'.
b.expr = CppExpr(b.tokens)
- for extra_include in extra_includes:
- replacement = CppStringTokenizer(extra_include)
- self.blocks.insert(2, Block(replacement.tokens, directive='include'))
-
def strip_space(s):
@@ -1647,7 +1675,7 @@
def get_blocks(self, lines):
blocks = BlockParser().parse(CppStringTokenizer('\n'.join(lines)))
- return map(lambda a: str(a), blocks)
+ return list(map(lambda a: str(a), blocks))
def test_hash(self):
self.assertEqual(self.get_blocks(["#error hello"]), ["#error hello"])
@@ -2020,7 +2048,7 @@
struct timeval val2;
};
"""
- self.assertEqual(self.parse(text, set(["remove"])), expected)
+ self.assertEqual(self.parse(text, {"remove": True}), expected)
def test_remove_struct_from_end(self):
text = """\
@@ -2039,7 +2067,7 @@
struct timeval val2;
};
"""
- self.assertEqual(self.parse(text, set(["remove"])), expected)
+ self.assertEqual(self.parse(text, {"remove": True}), expected)
def test_remove_minimal_struct(self):
text = """\
@@ -2047,7 +2075,7 @@
};
"""
expected = "";
- self.assertEqual(self.parse(text, set(["remove"])), expected)
+ self.assertEqual(self.parse(text, {"remove": True}), expected)
def test_remove_struct_with_struct_fields(self):
text = """\
@@ -2067,7 +2095,7 @@
struct remove val2;
};
"""
- self.assertEqual(self.parse(text, set(["remove"])), expected)
+ self.assertEqual(self.parse(text, {"remove": True}), expected)
def test_remove_consecutive_structs(self):
text = """\
@@ -2099,7 +2127,7 @@
struct timeval val2;
};
"""
- self.assertEqual(self.parse(text, set(["remove1", "remove2"])), expected)
+ self.assertEqual(self.parse(text, {"remove1": True, "remove2": True}), expected)
def test_remove_multiple_structs(self):
text = """\
@@ -2132,7 +2160,101 @@
int val;
};
"""
- self.assertEqual(self.parse(text, set(["remove1", "remove2"])), expected)
+ self.assertEqual(self.parse(text, {"remove1": True, "remove2": True}), expected)
+
+ def test_remove_struct_with_inline_structs(self):
+ text = """\
+struct remove {
+ int val1;
+ int val2;
+ struct {
+ int val1;
+ struct {
+ int val1;
+ } level2;
+ } level1;
+};
+struct something {
+ struct timeval val1;
+ struct timeval val2;
+};
+"""
+ expected = """\
+struct something {
+ struct timeval val1;
+ struct timeval val2;
+};
+"""
+ self.assertEqual(self.parse(text, {"remove": True}), expected)
+
+ def test_remove_struct_across_blocks(self):
+ text = """\
+struct remove {
+ int val1;
+ int val2;
+#ifdef PARAMETER1
+ PARAMETER1
+#endif
+#ifdef PARAMETER2
+ PARAMETER2
+#endif
+};
+struct something {
+ struct timeval val1;
+ struct timeval val2;
+};
+"""
+ expected = """\
+struct something {
+ struct timeval val1;
+ struct timeval val2;
+};
+"""
+ self.assertEqual(self.parse(text, {"remove": True}), expected)
+
+ def test_remove_struct_across_blocks_multiple_structs(self):
+ text = """\
+struct remove1 {
+ int val1;
+ int val2;
+#ifdef PARAMETER1
+ PARAMETER1
+#endif
+#ifdef PARAMETER2
+ PARAMETER2
+#endif
+};
+struct remove2 {
+};
+struct something {
+ struct timeval val1;
+ struct timeval val2;
+};
+"""
+ expected = """\
+struct something {
+ struct timeval val1;
+ struct timeval val2;
+};
+"""
+ self.assertEqual(self.parse(text, {"remove1": True, "remove2": True}), expected)
+
+ def test_remove_multiple_struct_and_add_includes(self):
+ text = """\
+struct remove1 {
+ int val1;
+ int val2;
+};
+struct remove2 {
+ struct timeval val1;
+ struct timeval val2;
+};
+"""
+ expected = """\
+#include <bits/remove1.h>
+#include <bits/remove2.h>
+"""
+ self.assertEqual(self.parse(text, {"remove1": False, "remove2": False}), expected)
class FullPathTest(unittest.TestCase):
diff --git a/libc/kernel/tools/defaults.py b/libc/kernel/tools/defaults.py
index 2089285..99bbc3e 100644
--- a/libc/kernel/tools/defaults.py
+++ b/libc/kernel/tools/defaults.py
@@ -35,16 +35,23 @@
"__kernel_old_timeval": "1",
}
-# this is the set of known kernel data structures we want to remove from
-# the final headers
-kernel_structs_to_remove = set(
- [
- # Remove the structures since they are still the same as
- # timeval, itimerval.
- "__kernel_old_timeval",
- "__kernel_old_itimerval",
- ]
- )
+# This is the set of known kernel data structures we want to remove from
+# the final headers. If the map value is False, that means that in
+# addition to removing the structure, add an #include <bits/STRUCT.h>
+# to the file.
+kernel_structs_to_remove = {
+ # Remove the structures since they are still the same as
+ # timeval, itimerval.
+ "__kernel_old_timeval": True,
+ "__kernel_old_itimerval": True,
+ # Replace all of the below structures with #include <bits/STRUCT.h>
+ "epoll_event": False,
+ "flock": False,
+ "flock64": False,
+ "in_addr": False,
+ "ip_mreq_source": False,
+ "ip_msfilter": False,
+ }
# define to true if you want to remove all defined(CONFIG_FOO) tests
# from the clean headers. testing shows that this is not strictly necessary
@@ -100,20 +107,6 @@
}
-# This is the set of struct definitions that we want to replace with
-# a #include of <bits/struct.h> instead.
-kernel_struct_replacements = set(
- [
- "epoll_event",
- "flock",
- "flock64",
- "in_addr",
- "ip_mreq_source",
- "ip_msfilter",
- ]
- )
-
-
# This is the set of known static inline functions that we want to keep
# in the final kernel headers.
kernel_known_generic_statics = set(
diff --git a/libc/kernel/tools/update_all.py b/libc/kernel/tools/update_all.py
index f2e78da..abc72a4 100755
--- a/libc/kernel/tools/update_all.py
+++ b/libc/kernel/tools/update_all.py
@@ -27,8 +27,14 @@
def ProcessFiles(updater, original_dir, modified_dir, src_rel_dir, update_rel_dir):
# Delete the old headers before updating to the new headers.
update_dir = os.path.join(get_kernel_dir(), update_rel_dir)
- shutil.rmtree(update_dir)
- os.mkdir(update_dir, 0o755)
+ for root, dirs, files in os.walk(update_dir, topdown=True):
+ for entry in files:
+ # BUILD is a special file that needs to be preserved.
+ if entry == "BUILD":
+ continue
+ os.remove(os.path.join(root, entry))
+ for entry in dirs:
+ shutil.rmtree(os.path.join(root, entry))
src_dir = os.path.normpath(os.path.join(original_dir, src_rel_dir))
src_dir_len = len(src_dir) + 1
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index 3c86ef5..c7e951d 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -29,19 +29,19 @@
#ifndef _PRIVATE_BIONIC_GLOBALS_H
#define _PRIVATE_BIONIC_GLOBALS_H
+#include <inttypes.h>
+#include <link.h>
+#include <platform/bionic/malloc.h>
+#include <pthread.h>
#include <stdatomic.h>
#include <sys/cdefs.h>
-#include <link.h>
-#include <pthread.h>
+#include "private/WriteProtected.h"
#include "private/bionic_allocator.h"
#include "private/bionic_elf_tls.h"
#include "private/bionic_fdsan.h"
#include "private/bionic_malloc_dispatch.h"
#include "private/bionic_vdso.h"
-#include "private/WriteProtected.h"
-
-#include <platform/bionic/malloc.h>
struct libc_globals {
vdso_entry vdso[VDSO_END];
@@ -114,6 +114,7 @@
HeapTaggingLevel initial_heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
bool initial_memtag_stack = false;
+ int64_t heap_tagging_upgrade_timer_sec = 0;
};
__LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals();
diff --git a/tests/mte_test.cpp b/tests/mte_test.cpp
index ade9532..5eb804f 100644
--- a/tests/mte_test.cpp
+++ b/tests/mte_test.cpp
@@ -32,6 +32,7 @@
reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(p.get()) + (1ULL << 56));
{
ScopedDisableMTE x;
+ // Test that nested ScopedDisableMTE does not reset MTE state.
{ ScopedDisableMTE y; }
#if defined(__aarch64__)
volatile int load ATTRIBUTE_UNUSED = *mistagged_p;
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 1acf989..21b3f6c 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -2940,28 +2940,38 @@
fclose(fp);
}
+#if defined(__LP64__)
static int64_t GetTotalRamGiB() {
struct sysinfo si;
sysinfo(&si);
return (static_cast<int64_t>(si.totalram) * si.mem_unit) / 1024 / 1024 / 1024;
}
+#endif
TEST(STDIO_TEST, fread_int_overflow) {
+#if defined(__LP64__)
if (GetTotalRamGiB() <= 4) GTEST_SKIP() << "not enough memory";
const size_t too_big_for_an_int = 0x80000000ULL;
std::vector<char> buf(too_big_for_an_int);
std::unique_ptr<FILE, decltype(&fclose)> fp{fopen("/dev/zero", "re"), fclose};
ASSERT_EQ(too_big_for_an_int, fread(&buf[0], 1, too_big_for_an_int, fp.get()));
+#else
+ GTEST_SKIP() << "32-bit can't allocate 2GiB";
+#endif
}
TEST(STDIO_TEST, fwrite_int_overflow) {
+#if defined(__LP64__)
if (GetTotalRamGiB() <= 4) GTEST_SKIP() << "not enough memory";
const size_t too_big_for_an_int = 0x80000000ULL;
std::vector<char> buf(too_big_for_an_int);
std::unique_ptr<FILE, decltype(&fclose)> fp{fopen("/dev/null", "we"), fclose};
ASSERT_EQ(too_big_for_an_int, fwrite(&buf[0], 1, too_big_for_an_int, fp.get()));
+#else
+ GTEST_SKIP() << "32-bit can't allocate 2GiB";
+#endif
}
TEST(STDIO_TEST, snprintf_b) {