Thu, 02 Aug 2018 03:54:51 -0700
8196882: VS2017 Hotspot Defined vsnprintf Function Causes C2084 Already Defined Compilation Error
Summary: Add os::vsnprintf and os::snprintf.
Reviewed-by: kbarrett, lfoltan, stuefe, mlarsson
1.1 --- a/src/os/posix/vm/os_posix.cpp Wed Aug 01 04:19:22 2018 -0400 1.2 +++ b/src/os/posix/vm/os_posix.cpp Thu Aug 02 03:54:51 2018 -0700 1.3 @@ -1,5 +1,5 @@ 1.4 /* 1.5 -* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. 1.6 +* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. 1.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.8 * 1.9 * This code is free software; you can redistribute it and/or modify it 1.10 @@ -166,6 +166,16 @@ 1.11 return aligned_base; 1.12 } 1.13 1.14 +int os::vsnprintf(char* buf, size_t len, const char* fmt, va_list args) { 1.15 + int result = ::vsnprintf(buf, len, fmt, args); 1.16 + // If an encoding error occurred (result < 0) then it's not clear 1.17 + // whether the buffer is NUL terminated, so ensure it is. 1.18 + if ((result < 0) && (len > 0)) { 1.19 + buf[len - 1] = '\0'; 1.20 + } 1.21 + return result; 1.22 +} 1.23 + 1.24 void os::Posix::print_load_average(outputStream* st) { 1.25 st->print("load average:"); 1.26 double loadavg[3];
2.1 --- a/src/os/windows/vm/os_windows.cpp Wed Aug 01 04:19:22 2018 -0400 2.2 +++ b/src/os/windows/vm/os_windows.cpp Thu Aug 02 03:54:51 2018 -0700 2.3 @@ -1837,6 +1837,42 @@ 2.4 st->cr(); 2.5 } 2.6 2.7 + 2.8 +int os::vsnprintf(char* buf, size_t len, const char* fmt, va_list args) { 2.9 +#if _MSC_VER >= 1900 2.10 + // Starting with Visual Studio 2015, vsnprint is C99 compliant. 2.11 + int result = ::vsnprintf(buf, len, fmt, args); 2.12 + // If an encoding error occurred (result < 0) then it's not clear 2.13 + // whether the buffer is NUL terminated, so ensure it is. 2.14 + if ((result < 0) && (len > 0)) { 2.15 + buf[len - 1] = '\0'; 2.16 + } 2.17 + return result; 2.18 +#else 2.19 + // Before Visual Studio 2015, vsnprintf is not C99 compliant, so use 2.20 + // _vsnprintf, whose behavior seems to be *mostly* consistent across 2.21 + // versions. However, when len == 0, avoid _vsnprintf too, and just 2.22 + // go straight to _vscprintf. The output is going to be truncated in 2.23 + // that case, except in the unusual case of empty output. More 2.24 + // importantly, the documentation for various versions of Visual Studio 2.25 + // are inconsistent about the behavior of _vsnprintf when len == 0, 2.26 + // including it possibly being an error. 2.27 + int result = -1; 2.28 + if (len > 0) { 2.29 + result = _vsnprintf(buf, len, fmt, args); 2.30 + // If output (including NUL terminator) is truncated, the buffer 2.31 + // won't be NUL terminated. Add the trailing NUL specified by C99. 2.32 + if ((result < 0) || (result >= (int) len)) { 2.33 + buf[len - 1] = '\0'; 2.34 + } 2.35 + } 2.36 + if (result < 0) { 2.37 + result = _vscprintf(fmt, args); 2.38 + } 2.39 + return result; 2.40 +#endif // _MSC_VER dispatch 2.41 +} 2.42 + 2.43 void os::print_signal_handlers(outputStream* st, char* buf, size_t buflen) { 2.44 // do nothing 2.45 }
3.1 --- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Wed Aug 01 04:19:22 2018 -0400 3.2 +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Thu Aug 02 03:54:51 2018 -0700 3.3 @@ -41,13 +41,13 @@ 3.4 int _cur; 3.5 3.6 void vappend(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0) { 3.7 - int res = vsnprintf(&_buffer[_cur], BUFFER_LEN - _cur, format, ap); 3.8 - if (res != -1) { 3.9 - _cur += res; 3.10 - } else { 3.11 + int res = os::vsnprintf(&_buffer[_cur], BUFFER_LEN - _cur, format, ap); 3.12 + if (res > BUFFER_LEN) { 3.13 DEBUG_ONLY(warning("buffer too small in LineBuffer");) 3.14 _buffer[BUFFER_LEN -1] = 0; 3.15 _cur = BUFFER_LEN; // vsnprintf above should not add to _buffer if we are called again 3.16 + } else if (res != -1) { 3.17 + _cur += res; 3.18 } 3.19 } 3.20
4.1 --- a/src/share/vm/oops/generateOopMap.cpp Wed Aug 01 04:19:22 2018 -0400 4.2 +++ b/src/share/vm/oops/generateOopMap.cpp Thu Aug 02 03:54:51 2018 -0700 4.3 @@ -1,5 +1,5 @@ 4.4 /* 4.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 4.6 + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. 4.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.8 * 4.9 * This code is free software; you can redistribute it and/or modify it 4.10 @@ -29,6 +29,7 @@ 4.11 #include "oops/symbol.hpp" 4.12 #include "runtime/handles.inline.hpp" 4.13 #include "runtime/java.hpp" 4.14 +#include "runtime/os.hpp" 4.15 #include "runtime/relocator.hpp" 4.16 #include "utilities/bitMap.inline.hpp" 4.17 #include "prims/methodHandles.hpp" 4.18 @@ -2133,10 +2134,10 @@ 4.19 void GenerateOopMap::error_work(const char *format, va_list ap) { 4.20 _got_error = true; 4.21 char msg_buffer[512]; 4.22 - vsnprintf(msg_buffer, sizeof(msg_buffer), format, ap); 4.23 + os::vsnprintf(msg_buffer, sizeof(msg_buffer), format, ap); 4.24 // Append method name 4.25 char msg_buffer2[512]; 4.26 - jio_snprintf(msg_buffer2, sizeof(msg_buffer2), "%s in method %s", msg_buffer, method()->name()->as_C_string()); 4.27 + os::snprintf(msg_buffer2, sizeof(msg_buffer2), "%s in method %s", msg_buffer, method()->name()->as_C_string()); 4.28 _exception = Exceptions::new_exception(Thread::current(), 4.29 vmSymbols::java_lang_LinkageError(), msg_buffer2); 4.30 } 4.31 @@ -2154,7 +2155,7 @@ 4.32 _got_error = true; 4.33 // Append method name 4.34 char msg_buffer2[512]; 4.35 - jio_snprintf(msg_buffer2, sizeof(msg_buffer2), "%s in method %s", msg, 4.36 + os::snprintf(msg_buffer2, sizeof(msg_buffer2), "%s in method %s", msg, 4.37 method()->name()->as_C_string()); 4.38 _exception = Exceptions::new_exception(Thread::current(), 4.39 vmSymbols::java_lang_LinkageError(), msg_buffer2);
5.1 --- a/src/share/vm/prims/jni.cpp Wed Aug 01 04:19:22 2018 -0400 5.2 +++ b/src/share/vm/prims/jni.cpp Thu Aug 02 03:54:51 2018 -0700 5.3 @@ -33,6 +33,7 @@ 5.4 #include "classfile/vmSymbols.hpp" 5.5 #include "interpreter/linkResolver.hpp" 5.6 #include "utilities/macros.hpp" 5.7 +#include "utilities/ostream.hpp" 5.8 #if INCLUDE_ALL_GCS 5.9 #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" 5.10 #endif // INCLUDE_ALL_GCS 5.11 @@ -5127,6 +5128,7 @@ 5.12 run_unit_test(GuardedMemory::test_guarded_memory()); 5.13 run_unit_test(AltHashing::test_alt_hash()); 5.14 run_unit_test(test_loggc_filename()); 5.15 + run_unit_test(test_snprintf()); 5.16 run_unit_test(TestNewSize_test()); 5.17 run_unit_test(TestKlass_test()); 5.18 run_unit_test(Test_linked_list());
6.1 --- a/src/share/vm/prims/jvm.cpp Wed Aug 01 04:19:22 2018 -0400 6.2 +++ b/src/share/vm/prims/jvm.cpp Thu Aug 02 03:54:51 2018 -0700 6.3 @@ -2915,16 +2915,12 @@ 6.4 6.5 ATTRIBUTE_PRINTF(3, 0) 6.6 int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { 6.7 - // see bug 4399518, 4417214 6.8 + // Reject count values that are negative signed values converted to 6.9 + // unsigned; see bug 4399518, 4417214 6.10 if ((intptr_t)count <= 0) return -1; 6.11 6.12 - int result = vsnprintf(str, count, fmt, args); 6.13 - // Note: on truncation vsnprintf(3) on Unix returns number of 6.14 - // characters which would have been written had the buffer been large 6.15 - // enough; on Windows, it returns -1. We handle both cases here and 6.16 - // always return -1, and perform null termination. 6.17 - if ((result > 0 && (size_t)result >= count) || result == -1) { 6.18 - str[count - 1] = '\0'; 6.19 + int result = os::vsnprintf(str, count, fmt, args); 6.20 + if (result > 0 && (size_t)result >= count) { 6.21 result = -1; 6.22 } 6.23
7.1 --- a/src/share/vm/runtime/os.cpp Wed Aug 01 04:19:22 2018 -0400 7.2 +++ b/src/share/vm/runtime/os.cpp Thu Aug 02 03:54:51 2018 -0700 7.3 @@ -108,6 +108,14 @@ 7.4 #endif 7.5 } 7.6 7.7 +int os::snprintf(char* buf, size_t len, const char* fmt, ...) { 7.8 + va_list args; 7.9 + va_start(args, fmt); 7.10 + int result = os::vsnprintf(buf, len, fmt, args); 7.11 + va_end(args); 7.12 + return result; 7.13 +} 7.14 + 7.15 // Fill in buffer with current local time as an ISO-8601 string. 7.16 // E.g., yyyy-mm-ddThh:mm:ss-zzzz. 7.17 // Returns buffer, or NULL if it failed.
8.1 --- a/src/share/vm/runtime/os.hpp Wed Aug 01 04:19:22 2018 -0400 8.2 +++ b/src/share/vm/runtime/os.hpp Thu Aug 02 03:54:51 2018 -0700 8.3 @@ -616,6 +616,9 @@ 8.4 static void *find_agent_function(AgentLibrary *agent_lib, bool check_lib, 8.5 const char *syms[], size_t syms_len); 8.6 8.7 + static int vsnprintf(char* buf, size_t len, const char* fmt, va_list args) ATTRIBUTE_PRINTF(3, 0); 8.8 + static int snprintf(char* buf, size_t len, const char* fmt, ...) ATTRIBUTE_PRINTF(3, 4); 8.9 + 8.10 // Print out system information; they are called by fatal error handler. 8.11 // Output format may be different on different platforms. 8.12 static void print_os_info(outputStream* st);
9.1 --- a/src/share/vm/utilities/exceptions.cpp Wed Aug 01 04:19:22 2018 -0400 9.2 +++ b/src/share/vm/utilities/exceptions.cpp Thu Aug 02 03:54:51 2018 -0700 9.3 @@ -30,6 +30,7 @@ 9.4 #include "runtime/init.hpp" 9.5 #include "runtime/java.hpp" 9.6 #include "runtime/javaCalls.hpp" 9.7 +#include "runtime/os.hpp" 9.8 #include "runtime/thread.inline.hpp" 9.9 #include "runtime/threadCritical.hpp" 9.10 #include "utilities/events.hpp" 9.11 @@ -246,8 +247,7 @@ 9.12 va_list ap; 9.13 va_start(ap, format); 9.14 char msg[max_msg_size]; 9.15 - vsnprintf(msg, max_msg_size, format, ap); 9.16 - msg[max_msg_size-1] = '\0'; 9.17 + os::vsnprintf(msg, max_msg_size, format, ap); 9.18 va_end(ap); 9.19 _throw_msg(thread, file, line, h_name, msg); 9.20 }
10.1 --- a/src/share/vm/utilities/globalDefinitions_visCPP.hpp Wed Aug 01 04:19:22 2018 -0400 10.2 +++ b/src/share/vm/utilities/globalDefinitions_visCPP.hpp Thu Aug 02 03:54:51 2018 -0700 10.3 @@ -1,5 +1,5 @@ 10.4 /* 10.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 10.6 + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. 10.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 10.8 * 10.9 * This code is free software; you can redistribute it and/or modify it 10.10 @@ -187,14 +187,6 @@ 10.11 #pragma warning( disable : 4996 ) // unsafe string functions. Same as define _CRT_SECURE_NO_WARNINGS/_CRT_SECURE_NO_DEPRICATE 10.12 #endif 10.13 10.14 -inline int vsnprintf(char* buf, size_t count, const char* fmt, va_list argptr) { 10.15 - // If number of characters written == count, Windows doesn't write a 10.16 - // terminating NULL, so we do it ourselves. 10.17 - int ret = _vsnprintf(buf, count, fmt, argptr); 10.18 - if (count > 0) buf[count-1] = '\0'; 10.19 - return ret; 10.20 -} 10.21 - 10.22 // Portability macros 10.23 #define PRAGMA_INTERFACE 10.24 #define PRAGMA_IMPLEMENTATION
11.1 --- a/src/share/vm/utilities/ostream.cpp Wed Aug 01 04:19:22 2018 -0400 11.2 +++ b/src/share/vm/utilities/ostream.cpp Thu Aug 02 03:54:51 2018 -0700 11.3 @@ -1,5 +1,5 @@ 11.4 /* 11.5 - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 11.6 + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. 11.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 11.8 * 11.9 * This code is free software; you can redistribute it and/or modify it 11.10 @@ -27,6 +27,7 @@ 11.11 #include "gc_implementation/shared/gcId.hpp" 11.12 #include "oops/oop.inline.hpp" 11.13 #include "runtime/arguments.hpp" 11.14 +#include "runtime/os.hpp" 11.15 #include "utilities/defaultStream.hpp" 11.16 #include "utilities/ostream.hpp" 11.17 #include "utilities/top.hpp" 11.18 @@ -89,6 +90,8 @@ 11.19 const char* format, va_list ap, 11.20 bool add_cr, 11.21 size_t& result_len) { 11.22 + assert(buflen >= 2, "buffer too small"); 11.23 + 11.24 const char* result; 11.25 if (add_cr) buflen--; 11.26 if (!strchr(format, '%')) { 11.27 @@ -101,18 +104,20 @@ 11.28 result = va_arg(ap, const char*); 11.29 result_len = strlen(result); 11.30 if (add_cr && result_len >= buflen) result_len = buflen-1; // truncate 11.31 - } else if (vsnprintf(buffer, buflen, format, ap) >= 0) { 11.32 + } else { 11.33 + int written = os::vsnprintf(buffer, buflen, format, ap); 11.34 + assert(written >= 0, "vsnprintf encoding error"); 11.35 result = buffer; 11.36 - result_len = strlen(result); 11.37 - } else { 11.38 - DEBUG_ONLY(warning("increase O_BUFLEN in ostream.hpp -- output truncated");) 11.39 - result = buffer; 11.40 - result_len = buflen - 1; 11.41 - buffer[result_len] = 0; 11.42 + if ((size_t)written < buflen) { 11.43 + result_len = written; 11.44 + } else { 11.45 + DEBUG_ONLY(warning("increase O_BUFLEN in ostream.hpp -- output truncated");) 11.46 + result_len = buflen - 1; 11.47 + } 11.48 } 11.49 if (add_cr) { 11.50 if (result != buffer) { 11.51 - strncpy(buffer, result, buflen); 11.52 + memcpy(buffer, result, result_len); 11.53 result = buffer; 11.54 } 11.55 buffer[result_len++] = '\n'; 11.56 @@ -578,6 +583,117 @@ 11.57 assert(o_result == NULL, err_msg("Too long file name after pid expansion should return NULL, but got '%s'", o_result)); 11.58 } 11.59 } 11.60 + 11.61 +////////////////////////////////////////////////////////////////////////////// 11.62 +// Test os::vsnprintf and friends. 11.63 + 11.64 +void check_snprintf_result(int expected, size_t limit, int actual, bool expect_count) { 11.65 + if (expect_count || ((size_t)expected < limit)) { 11.66 + assert(expected == actual, "snprintf result not expected value"); 11.67 + } else { 11.68 + // Make this check more permissive for jdk8u, don't assert that actual == 0. 11.69 + // e.g. jio_vsnprintf_wrapper and jio_snprintf return -1 when expected >= limit 11.70 + if (expected >= (int) limit) { 11.71 + assert(actual == -1, "snprintf result should be -1 for expected >= limit"); 11.72 + } else { 11.73 + assert(actual > 0, "snprintf result should be >0 for expected < limit"); 11.74 + } 11.75 + } 11.76 +} 11.77 + 11.78 +// PrintFn is expected to be int (*)(char*, size_t, const char*, ...). 11.79 +// But jio_snprintf is a C-linkage function with that signature, which 11.80 +// has a different type on some platforms (like Solaris). 11.81 +template<typename PrintFn> 11.82 +void test_snprintf(PrintFn pf, bool expect_count) { 11.83 + const char expected[] = "abcdefghijklmnopqrstuvwxyz"; 11.84 + const int expected_len = sizeof(expected) - 1; 11.85 + const size_t padding_size = 10; 11.86 + char buffer[2 * (sizeof(expected) + padding_size)]; 11.87 + char check_buffer[sizeof(buffer)]; 11.88 + const char check_char = '1'; // Something not in expected. 11.89 + memset(check_buffer, check_char, sizeof(check_buffer)); 11.90 + const size_t sizes_to_test[] = { 11.91 + sizeof(buffer) - padding_size, // Fits, with plenty of space to spare. 11.92 + sizeof(buffer)/2, // Fits, with space to spare. 11.93 + sizeof(buffer)/4, // Doesn't fit. 11.94 + sizeof(expected) + padding_size + 1, // Fits, with a little room to spare 11.95 + sizeof(expected) + padding_size, // Fits exactly. 11.96 + sizeof(expected) + padding_size - 1, // Doesn't quite fit. 11.97 + 2, // One char + terminating NUL. 11.98 + 1, // Only space for terminating NUL. 11.99 + 0 }; // No space at all. 11.100 + for (unsigned i = 0; i < ARRAY_SIZE(sizes_to_test); ++i) { 11.101 + memset(buffer, check_char, sizeof(buffer)); // To catch stray writes. 11.102 + size_t test_size = sizes_to_test[i]; 11.103 + ResourceMark rm; 11.104 + stringStream s; 11.105 + s.print("test_size: " SIZE_FORMAT, test_size); 11.106 + size_t prefix_size = padding_size; 11.107 + guarantee(test_size <= (sizeof(buffer) - prefix_size), "invariant"); 11.108 + size_t write_size = MIN2(sizeof(expected), test_size); 11.109 + size_t suffix_size = sizeof(buffer) - prefix_size - write_size; 11.110 + char* write_start = buffer + prefix_size; 11.111 + char* write_end = write_start + write_size; 11.112 + 11.113 + int result = pf(write_start, test_size, "%s", expected); 11.114 + 11.115 + check_snprintf_result(expected_len, test_size, result, expect_count); 11.116 + 11.117 + // Verify expected output. 11.118 + if (test_size > 0) { 11.119 + assert(0 == strncmp(write_start, expected, write_size - 1), "strncmp failure"); 11.120 + // Verify terminating NUL of output. 11.121 + assert('\0' == write_start[write_size - 1], "null terminator failure"); 11.122 + } else { 11.123 + guarantee(test_size == 0, "invariant"); 11.124 + guarantee(write_size == 0, "invariant"); 11.125 + guarantee(prefix_size + suffix_size == sizeof(buffer), "invariant"); 11.126 + guarantee(write_start == write_end, "invariant"); 11.127 + } 11.128 + 11.129 + // Verify no scribbling on prefix or suffix. 11.130 + assert(0 == strncmp(buffer, check_buffer, prefix_size), "prefix scribble"); 11.131 + assert(0 == strncmp(write_end, check_buffer, suffix_size), "suffix scribble"); 11.132 + } 11.133 + 11.134 + // Special case of 0-length buffer with empty (except for terminator) output. 11.135 + check_snprintf_result(0, 0, pf(NULL, 0, "%s", ""), expect_count); 11.136 + check_snprintf_result(0, 0, pf(NULL, 0, ""), expect_count); 11.137 +} 11.138 + 11.139 +// This is probably equivalent to os::snprintf, but we're being 11.140 +// explicit about what we're testing here. 11.141 +static int vsnprintf_wrapper(char* buf, size_t len, const char* fmt, ...) { 11.142 + va_list args; 11.143 + va_start(args, fmt); 11.144 + int result = os::vsnprintf(buf, len, fmt, args); 11.145 + va_end(args); 11.146 + return result; 11.147 +} 11.148 + 11.149 +// These are declared in jvm.h; test here, with related functions. 11.150 +extern "C" { 11.151 +int jio_vsnprintf(char*, size_t, const char*, va_list); 11.152 +int jio_snprintf(char*, size_t, const char*, ...); 11.153 +} 11.154 + 11.155 +// This is probably equivalent to jio_snprintf, but we're being 11.156 +// explicit about what we're testing here. 11.157 +static int jio_vsnprintf_wrapper(char* buf, size_t len, const char* fmt, ...) { 11.158 + va_list args; 11.159 + va_start(args, fmt); 11.160 + int result = jio_vsnprintf(buf, len, fmt, args); 11.161 + va_end(args); 11.162 + return result; 11.163 +} 11.164 + 11.165 +void test_snprintf() { 11.166 + test_snprintf(vsnprintf_wrapper, true); 11.167 + test_snprintf(os::snprintf, true); 11.168 + test_snprintf(jio_vsnprintf_wrapper, false); // jio_vsnprintf returns -1 on error including exceeding buffer size 11.169 + test_snprintf(jio_snprintf, false); // jio_snprintf calls jio_vsnprintf 11.170 +} 11.171 #endif // PRODUCT 11.172 11.173 fileStream::fileStream(const char* file_name) {
12.1 --- a/src/share/vm/utilities/ostream.hpp Wed Aug 01 04:19:22 2018 -0400 12.2 +++ b/src/share/vm/utilities/ostream.hpp Thu Aug 02 03:54:51 2018 -0700 12.3 @@ -258,6 +258,7 @@ 12.4 #ifndef PRODUCT 12.5 // unit test for checking -Xloggc:<filename> parsing result 12.6 void test_loggc_filename(); 12.7 +void test_snprintf(); 12.8 #endif 12.9 12.10 void ostream_init();