1
1
#include " env-inl.h"
2
+ #include " node_errors.h"
2
3
#include " node_external_reference.h"
3
4
#include " node_internals.h"
4
5
#include " util-inl.h"
12
13
#include < unistd.h> // setuid, getuid
13
14
#endif
14
15
#ifdef __linux__
16
+ #include < dlfcn.h> // dlsym()
15
17
#include < linux/capability.h>
16
18
#include < sys/auxv.h>
17
19
#include < sys/syscall.h>
@@ -231,6 +233,45 @@ static gid_t gid_by_name(Isolate* isolate, Local<Value> value) {
231
233
}
232
234
}
233
235
236
+ #ifdef __linux__
237
+ extern " C" {
238
+ int uv__node_patch_is_using_io_uring (void );
239
+
240
+ int uv__node_patch_is_using_io_uring (void ) __attribute__((weak));
241
+
242
+ typedef int (*is_using_io_uring_fn)(void );
243
+ }
244
+ #endif // __linux__
245
+
246
+ static bool UvMightBeUsingIoUring () {
247
+ #ifdef __linux__
248
+ // Support for io_uring is only included in libuv 1.45.0 and later, and only
249
+ // on Linux (and Android, but there it is always disabled). The patch that we
250
+ // apply to libuv to work around the io_uring security issue adds a function
251
+ // that tells us whether io_uring is being used. If that function is not
252
+ // present, we assume that we are dynamically linking against an unpatched
253
+ // version.
254
+ static std::atomic<is_using_io_uring_fn> check =
255
+ uv__node_patch_is_using_io_uring;
256
+ if (check == nullptr ) {
257
+ check = reinterpret_cast <is_using_io_uring_fn>(
258
+ dlsym (RTLD_DEFAULT, " uv__node_patch_is_using_io_uring" ));
259
+ }
260
+ return uv_version () >= 0x012d00u && (check == nullptr || (*check)());
261
+ #else
262
+ return false ;
263
+ #endif
264
+ }
265
+
266
+ static bool ThrowIfUvMightBeUsingIoUring (Environment* env, const char * fn) {
267
+ if (UvMightBeUsingIoUring ()) {
268
+ node::THROW_ERR_INVALID_STATE (
269
+ env, " %s() disabled: io_uring may be enabled. See CVE-2024-22017." , fn);
270
+ return true ;
271
+ }
272
+ return false ;
273
+ }
274
+
234
275
static void GetUid (const FunctionCallbackInfo<Value>& args) {
235
276
Environment* env = Environment::GetCurrent (args);
236
277
CHECK (env->has_run_bootstrapping_code ());
@@ -266,6 +307,8 @@ static void SetGid(const FunctionCallbackInfo<Value>& args) {
266
307
CHECK_EQ (args.Length (), 1 );
267
308
CHECK (args[0 ]->IsUint32 () || args[0 ]->IsString ());
268
309
310
+ if (ThrowIfUvMightBeUsingIoUring (env, " setgid" )) return ;
311
+
269
312
gid_t gid = gid_by_name (env->isolate (), args[0 ]);
270
313
271
314
if (gid == gid_not_found) {
@@ -285,6 +328,8 @@ static void SetEGid(const FunctionCallbackInfo<Value>& args) {
285
328
CHECK_EQ (args.Length (), 1 );
286
329
CHECK (args[0 ]->IsUint32 () || args[0 ]->IsString ());
287
330
331
+ if (ThrowIfUvMightBeUsingIoUring (env, " setegid" )) return ;
332
+
288
333
gid_t gid = gid_by_name (env->isolate (), args[0 ]);
289
334
290
335
if (gid == gid_not_found) {
@@ -304,6 +349,8 @@ static void SetUid(const FunctionCallbackInfo<Value>& args) {
304
349
CHECK_EQ (args.Length (), 1 );
305
350
CHECK (args[0 ]->IsUint32 () || args[0 ]->IsString ());
306
351
352
+ if (ThrowIfUvMightBeUsingIoUring (env, " setuid" )) return ;
353
+
307
354
uid_t uid = uid_by_name (env->isolate (), args[0 ]);
308
355
309
356
if (uid == uid_not_found) {
@@ -323,6 +370,8 @@ static void SetEUid(const FunctionCallbackInfo<Value>& args) {
323
370
CHECK_EQ (args.Length (), 1 );
324
371
CHECK (args[0 ]->IsUint32 () || args[0 ]->IsString ());
325
372
373
+ if (ThrowIfUvMightBeUsingIoUring (env, " seteuid" )) return ;
374
+
326
375
uid_t uid = uid_by_name (env->isolate (), args[0 ]);
327
376
328
377
if (uid == uid_not_found) {
@@ -363,6 +412,8 @@ static void SetGroups(const FunctionCallbackInfo<Value>& args) {
363
412
CHECK_EQ (args.Length (), 1 );
364
413
CHECK (args[0 ]->IsArray ());
365
414
415
+ if (ThrowIfUvMightBeUsingIoUring (env, " setgroups" )) return ;
416
+
366
417
Local<Array> groups_list = args[0 ].As <Array>();
367
418
size_t size = groups_list->Length ();
368
419
MaybeStackBuffer<gid_t , 64 > groups (size);
@@ -394,6 +445,8 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) {
394
445
CHECK (args[0 ]->IsUint32 () || args[0 ]->IsString ());
395
446
CHECK (args[1 ]->IsUint32 () || args[1 ]->IsString ());
396
447
448
+ if (ThrowIfUvMightBeUsingIoUring (env, " initgroups" )) return ;
449
+
397
450
Utf8Value arg0 (env->isolate (), args[0 ]);
398
451
gid_t extra_group;
399
452
bool must_free;
0 commit comments