-
-
Notifications
You must be signed in to change notification settings - Fork 849
Description
Issue Summary
When using both sharp and sqlite3 native modules together on ARM64 Linux, loading sqlite3 before sharp causes a segmentation fault when subsequently executing SQLite queries. Loading sharp first prevents the crash.
Steps to Reproduce
Environment
- OS: Kali Linux (Raspberry Pi 5)
- Architecture: ARM64 (aarch64)
- Kernel: 6.12.34+rpt-rpi-2712
- Page Size: 16KB (
getconf PAGE_SIZEreturns 16384) - Node.js: v22.21.1
- sharp: 0.34.5
- sqlite3: 5.1.7
Description
There appears to be a conflict between the native bindings of sharp (which uses libvips) and sqlite3 on ARM64 Linux systems. When sqlite3 is initialized first and queries are executed successfully, subsequently loading sharp corrupts some internal state. Any SQLite query executed after sharp is loaded results in a segmentation fault.
The issue does not occur when sharp is loaded before sqlite3 is initialized.
Steps to Reproduce
- Set up an ARM64 Linux system (tested on Raspberry Pi 5 with Kali Linux)
- Install Node.js v22.x
- Create a test project and install both modules:
mkdir sharp-sqlite-test
cd sharp-sqlite-test
npm init -y
npm install sharp sqlite3- Run the bug demonstration script (see below)
Minimal Reproduction - Bug Demonstration
Save as bug-demo.js:
#!/usr/bin/env node
/**
* Demonstrates sharp + sqlite3 segfault on ARM64 Linux
*
* This script will crash with a segmentation fault on affected systems.
* The crash occurs because sqlite3 is loaded before sharp.
*/
const sqlite3 = require('sqlite3').verbose();
console.log('Node:', process.version);
console.log('Arch:', process.arch);
console.log('Platform:', process.platform);
console.log('');
// Create an in-memory database
const db = new sqlite3.Database(':memory:');
console.log('[1] sqlite3 loaded and database created');
// Execute a query - this works fine
db.get('SELECT 1 as test', [], (err, row) => {
if (err) {
console.error('Query error:', err);
process.exit(1);
}
console.log('[2] First query OK:', row);
// Now load sharp AFTER sqlite3 is already initialized
console.log('[3] Loading sharp...');
const sharp = require('sharp');
console.log('[4] sharp loaded');
// This query will cause a segmentation fault
console.log('[5] Executing second query (will crash)...');
db.get('SELECT 2 as test', [], (err, row) => {
// This callback is never reached
console.log('[6] Second query OK:', row);
console.log('SUCCESS - No crash!');
process.exit(0);
});
});Run with:
node bug-demo.jsExpected output on affected systems:
Node: v22.21.1
Arch: arm64
Platform: linux
[1] sqlite3 loaded and database created
[2] First query OK: { test: 1 }
[3] Loading sharp...
[4] sharp loaded
[5] Executing second query (will crash)...
zsh: segmentation fault node bug-demo.js
Workaround - Loading sharp First
Save as fix-demo.js:
#!/usr/bin/env node
/**
* Demonstrates the fix: load sharp BEFORE sqlite3
*
* By loading sharp first, the segfault is avoided.
*/
// WORKAROUND: Load sharp BEFORE sqlite3
console.log('[1] Loading sharp FIRST...');
const sharp = require('sharp');
console.log('[2] sharp loaded');
const sqlite3 = require('sqlite3').verbose();
console.log('Node:', process.version);
console.log('Arch:', process.arch);
console.log('Platform:', process.platform);
console.log('');
// Create an in-memory database
const db = new sqlite3.Database(':memory:');
console.log('[3] sqlite3 loaded and database created');
// Execute a query - this works fine
db.get('SELECT 1 as test', [], (err, row) => {
if (err) {
console.error('Query error:', err);
process.exit(1);
}
console.log('[4] First query OK:', row);
// Execute another query - this also works now
console.log('[5] Executing second query...');
db.get('SELECT 2 as test', [], (err, row) => {
if (err) {
console.error('Query error:', err);
process.exit(1);
}
console.log('[6] Second query OK:', row);
console.log('');
console.log('SUCCESS - No crash when sharp is loaded first!');
process.exit(0);
});
});Run with:
node fix-demo.jsExpected output:
[1] Loading sharp FIRST...
[2] sharp loaded
Node: v22.21.1
Arch: arm64
Platform: linux
[3] sqlite3 loaded and database created
[4] First query OK: { test: 1 }
[5] Executing second query...
[6] Second query OK: { test: 2 }
SUCCESS - No crash when sharp is loaded first!
Analysis
The issue appears to be related to how the native bindings of these two modules interact on ARM64 Linux:
-
Memory allocator conflict: Both libvips (used by sharp) and SQLite have their own memory management. Loading them in a specific order may cause conflicts.
-
Thread pool interference: Both modules use thread pools for async operations. Sharp's initialization may interfere with SQLite's thread pool.
-
16KB page size: This system uses 16KB pages instead of the typical 4KB. This may exacerbate memory alignment or mapping issues between native modules.
Affected Software
This bug was discovered while running [ENiGMA½ BBS](https://github.com/NuSkooler/enigma-bbs) on a Raspberry Pi 5 with Kali Linux. The BBS uses both sharp (via avatar-generator) and sqlite3 for its database.
Workaround
Add require('sharp') at the very top of your application's entry point, before any other modules that might load sqlite3:
#!/usr/bin/env node
// WORKAROUND: Must load sharp before sqlite3 to avoid ARM64 segfault
require('sharp');
// ... rest of your applicationAdditional Information
- Rebuilding sqlite3 from source (
npm install sqlite3 --build-from-source) does not fix the issue - Rebuilding sharp from source does not fix the issue
- The issue is timing-independent (adding delays doesn't help)
- The issue only manifests on ARM64; x86_64 systems are not affected
- Running an intermediate SQLite query between loading sqlite3 and sharp can sometimes prevent the crash, suggesting the corruption occurs during sharp's initialization
Version
5.1.7
Node.js Version
22.21.1
How did you install the library?
Kali Linux (Raspberry Pi 5) ARM64 (aarch64)