netty5/transport-native-unix-common/src/main/c/netty_unix_util.c
Scott Mitchell ac023da16d Correctly handle overflow in Native.kevent(...) when EINTR is detected (#9024)
Motivation:
When kevent(...) returns with EINTR we do not correctly decrement the timespec
structure contents to account for the time duration. This may lead to negative
values for tv_nsec which will result in an EINVAL and raise an IOException to
the event loop selection loop.

Modifications:
Correctly calculate new timeoutTs when EINTR is detected

Result:
Fixes #9013.
2019-04-10 11:04:13 +02:00

198 lines
6.2 KiB
C

/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "netty_unix_util.h"
static const uint64_t NETTY_BILLION = 1000000000L;
#ifdef NETTY_USE_MACH_INSTEAD_OF_CLOCK
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif /* NETTY_USE_MACH_INSTEAD_OF_CLOCK */
char* netty_unix_util_prepend(const char* prefix, const char* str) {
if (prefix == NULL) {
char* result = (char*) malloc(sizeof(char) * (strlen(str) + 1));
strcpy(result, str);
return result;
}
char* result = (char*) malloc(sizeof(char) * (strlen(prefix) + strlen(str) + 1));
strcpy(result, prefix);
strcat(result, str);
return result;
}
char* netty_unix_util_rstrstr(char* s1rbegin, const char* s1rend, const char* s2) {
size_t s2len = strlen(s2);
char *s = s1rbegin - s2len;
for (; s >= s1rend; --s) {
if (strncmp(s, s2, s2len) == 0) {
return s;
}
}
return NULL;
}
static char* netty_unix_util_strstr_last(const char* haystack, const char* needle) {
char* prevptr = NULL;
char* ptr = (char*) haystack;
while ((ptr = strstr(ptr, needle)) != NULL) {
// Just store the ptr and continue searching.
prevptr = ptr;
++ptr;
}
return prevptr;
}
char* netty_unix_util_parse_package_prefix(const char* libraryPathName, const char* libraryName, jint* status) {
char* packageNameEnd = netty_unix_util_strstr_last(libraryPathName, libraryName);
if (packageNameEnd == NULL) {
*status = JNI_ERR;
return NULL;
}
char* packagePrefix = netty_unix_util_rstrstr(packageNameEnd, libraryPathName, "lib");
if (packagePrefix == NULL) {
*status = JNI_ERR;
return NULL;
}
packagePrefix += 3;
if (packagePrefix == packageNameEnd) {
return NULL;
}
// packagePrefix length is > 0
// Make a copy so we can modify the value without impacting libraryPathName.
size_t packagePrefixLen = packageNameEnd - packagePrefix;
packagePrefix = strndup(packagePrefix, packagePrefixLen);
// Make sure the packagePrefix is in the correct format for the JNI functions it will be used with.
char* temp = packagePrefix;
packageNameEnd = packagePrefix + packagePrefixLen;
// Package names must be sanitized, in JNI packages names are separated by '/' characters.
for (; temp != packageNameEnd; ++temp) {
if (*temp == '_') {
*temp = '/';
}
}
// Make sure packagePrefix is terminated with the '/' JNI package separator.
if(*(--temp) != '/') {
temp = packagePrefix;
packagePrefix = netty_unix_util_prepend(packagePrefix, "/");
free(temp);
}
return packagePrefix;
}
// util methods
uint64_t netty_unix_util_timespec_elapsed_ns(const struct timespec* begin, const struct timespec* end) {
return NETTY_BILLION * (end->tv_sec - begin->tv_sec) + (end->tv_nsec - begin->tv_nsec);
}
jboolean netty_unix_util_timespec_subtract_ns(struct timespec* ts, uint64_t nanos) {
const uint64_t seconds = nanos / NETTY_BILLION;
nanos -= seconds * NETTY_BILLION;
// If there are too many nanos we steal from seconds to avoid underflow on nanos. This way we
// only have to worry about underflow on tv_sec.
if (nanos > ts->tv_nsec) {
--(ts->tv_sec);
ts->tv_nsec += NETTY_BILLION;
}
const jboolean underflow = ts->tv_sec < seconds;
ts->tv_sec -= seconds;
ts->tv_nsec -= nanos;
return underflow;
}
int netty_unix_util_clock_gettime(clockid_t clockId, struct timespec* tp) {
#ifdef NETTY_USE_MACH_INSTEAD_OF_CLOCK
uint64_t timeNs;
switch (clockId) {
case CLOCK_MONOTONIC_COARSE:
timeNs = mach_approximate_time();
break;
case CLOCK_MONOTONIC:
timeNs = mach_absolute_time();
break;
default:
errno = EINVAL;
return -1;
}
// NOTE: this could overflow if time_t is backed by a 32 bit number.
tp->tv_sec = timeNs / NETTY_BILLION;
tp->tv_nsec = timeNs - tp->tv_sec * NETTY_BILLION; // avoid using modulo if not necessary
return 0;
#else
return clock_gettime(clockId, tp);
#endif /* NETTY_USE_MACH_INSTEAD_OF_CLOCK */
}
jboolean netty_unix_util_initialize_wait_clock(clockid_t* clockId) {
struct timespec ts;
// First try to get a monotonic clock, as we effectively measure execution time and don't want the underlying clock
// moving unexpectedly/abruptly.
#ifdef CLOCK_MONOTONIC_COARSE
*clockId = CLOCK_MONOTONIC_COARSE;
if (netty_unix_util_clock_gettime(*clockId, &ts) == 0) {
return JNI_TRUE;
}
#endif
#ifdef CLOCK_MONOTONIC_RAW
*clockId = CLOCK_MONOTONIC_RAW;
if (netty_unix_util_clock_gettime(*clockId, &ts) == 0) {
return JNI_TRUE;
}
#endif
#ifdef CLOCK_MONOTONIC
*clockId = CLOCK_MONOTONIC;
if (netty_unix_util_clock_gettime(*clockId, &ts) == 0) {
return JNI_TRUE;
}
#endif
// Fallback to realtime ... in this case we are subject to clock movements on the system.
#ifdef CLOCK_REALTIME_COARSE
*clockId = CLOCK_REALTIME_COARSE;
if (netty_unix_util_clock_gettime(*clockId, &ts) == 0) {
return JNI_TRUE;
}
#endif
#ifdef CLOCK_REALTIME
*clockId = CLOCK_REALTIME;
if (netty_unix_util_clock_gettime(*clockId, &ts) == 0) {
return JNI_TRUE;
}
#endif
return JNI_FALSE;
}
jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods) {
char* nettyClassName = netty_unix_util_prepend(packagePrefix, className);
jclass nativeCls = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (nativeCls == NULL) {
return JNI_ERR;
}
return (*env)->RegisterNatives(env, nativeCls, methods, numMethods);
}