koffi 2.8.4 → 2.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  ### Koffi 2.8
6
6
 
7
+ #### Koffi 2.8.6 (2024-04-12)
8
+
9
+ - Support [loading library](functions.md#loading-options) with RTLD_DEEPBIND where supported
10
+
11
+ #### Koffi 2.8.5 (2024-04-11)
12
+
13
+ - Prevent obviously invalid type and member names
14
+ - Fix possible infinite loop / UB for `koffi.load()` errors on POSIX systems
15
+ - Fix null return value instead of exception for some errors in `koffi.load()` on Windows
16
+
7
17
  #### Koffi 2.8.4 (2024-04-09)
8
18
 
9
19
  - Use simpler workaround for Node 20.12+ and 21.6+ to avoid excessive memory use
package/README.md CHANGED
@@ -29,6 +29,12 @@ You can consult the [changelog](https://koffi.dev/changelog) on the official web
29
29
 
30
30
  Major version increments can include breaking API changes, use the [migration guide](https://koffi.dev/changelog#migration-guide) for more information.
31
31
 
32
+ # Build manually
33
+
34
+ Koffi is built with a custom CMake-wrapper called CNoke, which also lives in this repository. Don't try to run CMake manually because it will fail.
35
+
36
+ Follow the [documented build instructions](https://koffi.dev/contribute#build-from-source) to build Koffi from source.
37
+
32
38
  # License
33
39
 
34
40
  This program is free software: you can redistribute it and/or modify it under the terms of the **MIT License**.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/doc/contribute.md CHANGED
@@ -25,7 +25,7 @@ First, make sure the following dependencies are met:
25
25
 
26
26
  - The "Desktop development with C++" workload from [Visual Studio 2022 or 2019](https://visualstudio.microsoft.com/downloads/) or the "C++ build tools" workload from the [Build Tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022), with the default optional components.
27
27
  - [CMake meta build system](https://cmake.org/)
28
- - [Node.js](https://nodejs.org/) 12 or later
28
+ - [Node.js](https://nodejs.org/) 16 or later
29
29
 
30
30
  Once this is done, run this command _from the test or the benchmark directory_ (depending on what you want to build):
31
31
 
@@ -41,7 +41,7 @@ Make sure the following dependencies are met:
41
41
  - `gcc` and `g++` >= 8.3 or newer
42
42
  - GNU Make 3.81 or newer
43
43
  - [CMake meta build system](https://cmake.org/)
44
- - [Node.js](https://nodejs.org/) 12 or later
44
+ - [Node.js](https://nodejs.org/) 16 or later
45
45
 
46
46
  Once this is done, run this command _from the test or the benchmark directory_ (depending on what you want to build):
47
47
 
@@ -64,6 +64,7 @@ node sync.js # Run synchronous unit tests
64
64
  node async.js # Run asynchronous unit tests
65
65
  node callbacks.js # Run callback unit tests
66
66
  node union.js # Run union unit tests
67
+ node posix.js # Run POSIX-specific unit tests (not for Windows)
67
68
  node win32.js # Run Windows-specific unit tests (only on Windows)
68
69
 
69
70
  node sqlite.js # Run SQLite integration tests
package/doc/functions.md CHANGED
@@ -21,14 +21,15 @@ On some platforms (such as with the [musl C library on Linux](https://wiki.musl-
21
21
 
22
22
  ## Loading options
23
23
 
24
- *New in Koffi 2.6 and Koffi 2.8.2*
24
+ *New in Koffi 2.6, changed in Koffi 2.8.2 and Koffi 2.8.6*
25
25
 
26
26
  The `load` function can take an optional object argument, with the following options:
27
27
 
28
28
  ```js
29
29
  const options = {
30
30
  lazy: true, // Use RTLD_LAZY (lazy-binding) on POSIX platforms (by default, use RTLD_NOW)
31
- global: true // Use RTLD_GLOBAL on POSIX platforms (by default, use RTLD_LOCAL)
31
+ global: true, // Use RTLD_GLOBAL on POSIX platforms (by default, use RTLD_LOCAL)
32
+ deep: true // Use RTLD_DEEPBIND if supported (Linux, FreeBSD)
32
33
  };
33
34
 
34
35
  const lib = koffi.load('/path/to/shared/library.so', options);
package/doc/start.md CHANGED
@@ -109,4 +109,12 @@ if (ret == IDYES)
109
109
 
110
110
  ## Bundling Koffi
111
111
 
112
- Please read the [dedicated page](packaging.md) for information about bundling and packaging applications using Koffi.
112
+ Please read the [dedicated page](packaging.md) for information about bundling and packaging applications using Koffi.
113
+
114
+ ## Build manually
115
+
116
+ Follow the [build instrutions](contribute.md#build-from-source) if you want to build the native Koffi code yourself.
117
+
118
+ ```{note}
119
+ This is only needed if you want to hack on Koffi. The official NPM package provide prebuilt binaries and you don't need to compile anything if you only want to use Koffi in Node.js.
120
+ ```
package/index.js CHANGED
@@ -378,8 +378,8 @@ var require_package = __commonJS({
378
378
  "build/dist/src/koffi/package.json"(exports2, module2) {
379
379
  module2.exports = {
380
380
  name: "koffi",
381
- version: "2.8.4",
382
- stable: "2.8.4",
381
+ version: "2.8.6",
382
+ stable: "2.8.6",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
package/indirect.js CHANGED
@@ -378,8 +378,8 @@ var require_package = __commonJS({
378
378
  "build/dist/src/koffi/package.json"(exports2, module2) {
379
379
  module2.exports = {
380
380
  name: "koffi",
381
- version: "2.8.4",
382
- stable: "2.8.4",
381
+ version: "2.8.6",
382
+ stable: "2.8.6",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.8.4",
4
- "stable": "2.8.4",
3
+ "version": "2.8.6",
4
+ "stable": "2.8.6",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -1935,7 +1935,7 @@ bool GetDebugFlag(const char *name)
1935
1935
  #ifndef NDEBUG
1936
1936
  const char *DebugLogContext(const char *filename, int line)
1937
1937
  {
1938
- static RG_THREAD_LOCAL LocalArray<char, 1024> buf;
1938
+ static thread_local LocalArray<char, 1024> buf;
1939
1939
 
1940
1940
  buf.len = Fmt(buf.data, " [%1:%2] ", filename, line).len;
1941
1941
 
@@ -21,8 +21,12 @@
21
21
 
22
22
  cmake_minimum_required(VERSION 3.6)
23
23
  cmake_policy(SET CMP0091 NEW)
24
- project(koffi C CXX ASM)
25
24
 
25
+ if(NOT NODE_JS_INCLUDE_DIRS)
26
+ message(FATAL_ERROR "Please use CNoke to build Koffi, follow instructions here: https://koffi.dev/contribute#build-from-source")
27
+ endif()
28
+
29
+ project(koffi C CXX ASM)
26
30
  find_package(CNoke)
27
31
 
28
32
  include(CheckCXXCompilerFlag)
@@ -200,6 +200,38 @@ static inline bool CheckAlignment(int64_t align)
200
200
  return valid;
201
201
  }
202
202
 
203
+ // Prevent simple mistakes but don't be too strict, the world is bigger than the US!
204
+ static bool IsNameValid(const char *name)
205
+ {
206
+ if (!name[0] || IsAsciiWhite(name[0]) || IsAsciiDigit(name[0])) [[unlikely]]
207
+ return false;
208
+
209
+ for (Size i = 1; name[i]; i++) {
210
+ if (IsAsciiWhite(name[i])) [[unlikely]]
211
+ return false;
212
+ }
213
+
214
+ return true;
215
+ }
216
+
217
+ static bool MapType(Napi::Env env, InstanceData *instance, const TypeInfo *type, const char *name)
218
+ {
219
+ if (!IsNameValid(name)) {
220
+ ThrowError<Napi::Error>(env, "Invalid type name '%1'", name);
221
+ return false;
222
+ }
223
+
224
+ bool inserted;
225
+ instance->types_map.TrySet(name, type, &inserted);
226
+
227
+ if (!inserted) {
228
+ ThrowError<Napi::Error>(env, "Duplicate type name '%1'", name);
229
+ return false;
230
+ }
231
+
232
+ return true;
233
+ }
234
+
203
235
  static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
204
236
  {
205
237
  Napi::Env env = info.Env();
@@ -245,13 +277,8 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
245
277
  if (named) {
246
278
  type->name = DuplicateString(name.Utf8Value().c_str(), &instance->str_alloc).ptr;
247
279
 
248
- bool inserted;
249
- instance->types_map.TrySet(type->name, type, &inserted);
250
-
251
- if (!inserted) {
252
- ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
280
+ if (!MapType(env, instance, type, type->name))
253
281
  return env.Null();
254
- }
255
282
  } else {
256
283
  type->name = Fmt(&instance->str_alloc, "<anonymous_%1>", instance->types.len).ptr;
257
284
  }
@@ -312,6 +339,11 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
312
339
  return env.Null();
313
340
  }
314
341
 
342
+ if (!IsNameValid(member.name)) {
343
+ ThrowError<Napi::Error>(env, "Invalid member name '%1'", member.name);
344
+ return env.Null();
345
+ }
346
+
315
347
  bool inserted;
316
348
  members.TrySet(member.name, &inserted);
317
349
 
@@ -391,13 +423,8 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
391
423
  if (named) {
392
424
  type->name = DuplicateString(name.Utf8Value().c_str(), &instance->str_alloc).ptr;
393
425
 
394
- bool inserted;
395
- instance->types_map.TrySet(type->name, type, &inserted);
396
-
397
- if (!inserted) {
398
- ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
426
+ if (!MapType(env, instance, type, type->name))
399
427
  return env.Null();
400
- }
401
428
  } else {
402
429
  type->name = Fmt(&instance->str_alloc, "<anonymous_%1>", instance->types.len).ptr;
403
430
  }
@@ -449,6 +476,11 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
449
476
  size = std::max(size, member.type->size);
450
477
  type->align = std::max(type->align, align);
451
478
 
479
+ if (!IsNameValid(member.name)) {
480
+ ThrowError<Napi::Error>(env, "Invalid member name '%1'", member.name);
481
+ return env.Null();
482
+ }
483
+
452
484
  bool inserted;
453
485
  members.TrySet(member.name, &inserted);
454
486
 
@@ -530,15 +562,8 @@ static Napi::Value CreateOpaqueType(const Napi::CallbackInfo &info)
530
562
  type->align = 0;
531
563
 
532
564
  // If the insert succeeds, we cannot fail anymore
533
- if (named) {
534
- bool inserted;
535
- instance->types_map.TrySet(type->name, type, &inserted);
536
-
537
- if (!inserted) {
538
- ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
539
- return env.Null();
540
- }
541
- }
565
+ if (named && !MapType(env, instance, type, type->name))
566
+ return env.Null();
542
567
  err_guard.Disable();
543
568
 
544
569
  return WrapType(env, instance, type);
@@ -594,14 +619,9 @@ static Napi::Value CreatePointerType(const Napi::CallbackInfo &info)
594
619
  memcpy((void *)copy, type, RG_SIZE(*type));
595
620
  copy->name = DuplicateString(name.c_str(), &instance->str_alloc).ptr;
596
621
 
597
- bool inserted;
598
- instance->types_map.TrySet(copy->name, copy, &inserted);
599
-
600
622
  // If the insert succeeds, we cannot fail anymore
601
- if (!inserted) {
602
- ThrowError<Napi::Error>(env, "Duplicate type name '%1'", copy->name);
623
+ if (!MapType(env, instance, copy, copy->name))
603
624
  return env.Null();
604
- }
605
625
  err_guard.Disable();
606
626
 
607
627
  type = copy;
@@ -1090,15 +1110,8 @@ static Napi::Value CreateTypeAlias(const Napi::CallbackInfo &info)
1090
1110
  return env.Null();
1091
1111
 
1092
1112
  // Alias the type
1093
- {
1094
- bool inserted;
1095
- instance->types_map.TrySet(alias, type, &inserted);
1096
-
1097
- if (!inserted) {
1098
- ThrowError<Napi::Error>(env, "Type name '%1' already exists", alias);
1099
- return env.Null();
1100
- }
1101
- }
1113
+ if (!MapType(env, instance, type, alias))
1114
+ return env.Null();
1102
1115
 
1103
1116
  return WrapType(env, instance, type);
1104
1117
  }
@@ -1724,6 +1737,9 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
1724
1737
 
1725
1738
  flags |= options.Get("lazy").ToBoolean() ? RTLD_LAZY : RTLD_NOW;
1726
1739
  flags |= options.Get("global").ToBoolean() ? RTLD_GLOBAL : RTLD_LOCAL;
1740
+ #ifdef RTLD_DEEPBIND
1741
+ flags |= options.Get("deep").ToBoolean() ? RTLD_DEEPBIND : 0;
1742
+ #endif
1727
1743
  } else {
1728
1744
  flags = RTLD_NOW | RTLD_LOCAL;
1729
1745
  }
@@ -1757,9 +1773,10 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
1757
1773
 
1758
1774
  if (StartsWith(msg, filename.c_str())) {
1759
1775
  msg += filename.length();
1760
- }
1761
- while (strchr(": ", msg[0])) {
1762
- msg++;
1776
+
1777
+ while (strchr(": ", msg[0]) && msg[0]) {
1778
+ msg++;
1779
+ }
1763
1780
  }
1764
1781
 
1765
1782
  ThrowError<Napi::Error>(env, "Failed to load shared library: %1", msg);
@@ -74,8 +74,10 @@ HANDLE LoadWindowsLibrary(Napi::Env env, Span<const char> path)
74
74
 
75
75
  Span<wchar_t> filename_w = AllocateSpan<wchar_t>(&temp_alloc, path.len + 1);
76
76
 
77
- if (ConvertUtf8ToWin32Wide(path, filename_w) < 0)
77
+ if (ConvertUtf8ToWin32Wide(path, filename_w) < 0) {
78
+ ThrowError<Napi::Error>(env, "Invalid path string");
78
79
  return nullptr;
80
+ }
79
81
 
80
82
  HMODULE module = LoadLibraryW(filename_w.ptr);
81
83
 
@@ -85,8 +87,10 @@ HANDLE LoadWindowsLibrary(Napi::Env env, Span<const char> path)
85
87
  Span<const char> filename = NormalizePath(path, GetWorkingDirectory(), &temp_alloc);
86
88
  Span<wchar_t> filename_w = AllocateSpan<wchar_t>(&temp_alloc, filename.len + 1);
87
89
 
88
- if (ConvertUtf8ToWin32Wide(filename, filename_w) < 0)
90
+ if (ConvertUtf8ToWin32Wide(filename, filename_w) < 0) {
91
+ ThrowError<Napi::Error>(env, "Invalid path string");
89
92
  return nullptr;
93
+ }
90
94
 
91
95
  module = LoadLibraryExW(filename_w.ptr, nullptr, flags);
92
96
  }