Repository: electron/electron (language: cpp)

Issue:
[Bug]: navigator.serial.requestPort crashing app
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

18.1.0

### What operating system are you using?

Other Linux

### Operating System Version

Arch Linux: Linux kyle-workstation 5.16.16-arch1-1 #1 SMP PREEMPT Mon, 21 Mar 2022 22:59:40 +0000 x86_64 GNU/Linux

### What arch are you using?

x86_64

### Last Known Working Electron version

N/A

### Expected Behavior

When the button is clicked, the serial port should connect to the device that is plugged in and print out the port permissions.

### Actual Behavior

When the serial device is plugged in to the machine running our built app, it crashes calling requestPort.

While creating the gist, I was unable to reproduce this just by calling requestPort, but by unplugging the device after calling requestPort or immediately calling getPorts after requestPort, it crashes with the same stack trace.

### Testcase Gist URL

https://gist.github.com/4c31f45fda81126709be74bbd80e52a3

### Additional Information

This might be an issue with the way the RFID serial device communicate over serial, since this method of connecting works with the barcode scanner we have.

Thank you for your time.


Tested Versions:
```
Gist Versions:
- 18.0.3 fails
- 18.1.0 fails

Our App Versions:
- 17.1.1 fails
- 18.0.4 fails
```
This happens with the RFID card readers we have, but not the Barcode Scanners. Both use the same serial interface to connect.

Fiddle Output (crashing on navigator.serial.getPort)
```
Saving files to temp directory...
Saved files to /tmp/tmp-384970-SUytkf3tFEwD
Installing node modules using npm: usb...
added 4 packages, and audited 5 packages in 504ms

found 0 vulnerabilities
Electron v18.1.0 started.
[393963:0421/110120.524663:WARNING:bluez_dbus_manager.cc(248)] Floss manager not present, cannot set Floss enable/disable.
[
  {
    portId: 'E99A31D64711FC0AD5792B9379C871DC',
    portName: 'ttyACM0',
    displayName: 'CDC RS232 Device',
    vendorId: '3111',
    productId: '9002'
  }
]
[393963:0421/110120.979544:WARNING:CONSOLE(53)] "%cElectron Security Warning (Insecure Content-Security-Policy) font-weight: bold; This renderer process has either no Content Security
  Policy set or a policy with "unsafe-eval" enabled. This exposes users of
  this app to unnecessary security risks.

For more information and help, consult
https://electronjs.org/docs/tutorial/security.
This warning will not show up
once the app is packaged.", source: node:electron/js2c/sandbox_bundle (53)
Received signal 11 SEGV_MAPERR 000000000000
#0 0x563022f96483 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x4d18482)
#1 0x56302302ea7a (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x4db0a79)
#2 0x7fe9f0451560 (/usr/lib/libc.so.6+0x4255f)
#3 0x5630203b63b4 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x21383b3)
#4 0x5630228364d9 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x45b84d8)
#5 0x563022837503 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x45b9502)
#6 0x563021125e8c (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x2ea7e8b)
#7 0x56302331740a (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x5099409)
#8 0x56302331c24c (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x509e24b)
#9 0x5630233187aa (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x509a7a9)
#10 0x56302332448c (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x50a648b)
#11 0x563023323c28 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x50a5c27)
#12 0x56302331c24c (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x509e24b)
#13 0x563023314d9e (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x5096d9d)
#14 0x5630233160ef (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x50980ee)
#15 0x56302333690e
(/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x50b890d)
#16 0x563022fe6ae6 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x4d68ae5)
#17 0x563023003159 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x4d85158)
#18 0x563022fb30df (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x4d350de)
#19 0x7fe9f195e163 (/usr/lib/libglib-2.0.so.0.7200.0+0x55162)
#20 0x3d3c0024bbc0 <unknown>
#21 0x563023003bc4 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x4d85bc3)
#22 0x563022fd0092 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x4d52091)
#23 0x5630222e826f (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x406a26e)
#24 0x5630222e9de2 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x406bde1)
#25 0x5630222e59de (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x40679dd)
#26 0x563020536f51 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x22b8f50)
#27 0x563020538448 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x22ba447)
#28 0x563020537f3b (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x22b9f3a)
#29 0x56302053557d (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x22b757c)
#30 0x563020535d64 (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x22b7d63)
#31 0x5630202c1a5b (/home/kyle/.config/Electron Fiddle/electron-bin/18.1.0/electron+0x2043a5a)
#32 0x7fe9f043c310 (/usr/lib/libc.so.6+0x2d30f)
  r8: 0000000000000005  r9: 000056301f8170c0 r10: 0000000000000020 r11: 000056301f8171c0
 r12: 0000000000000000 r13: 00003d3c01cec800 r14: 00003d3c01cec820 r15: 0000000000000024
  di: 000056301f40bf9d  si: 00003d3c01d04861  bp: 00007ffd184a1dd0  bx: 000056301f40bf9d
  dx: 00003d3c01d04860  ax: 0000000000000000  cx: 0000000000000074  sp: 00007ffd184a1d10
  ip: 000056302037e3c5 efl: 0000000000010202 cgf: 002b000000000033 erf: 0000000000000004
 trp: 000000000000000e msk: 0000000000000000 cr2: 0000000000000000
[end of stack trace]

Electron exited with signal SIGSEGV.
```


Built App stack trace (navigator.serial.requestPort)
```
Received signal 11 SEGV_MAPERR 000000000000
#0 0x561dcb863463 (/tmp/app/path+0x4d1b462)
#1 0x561dcb8fba5a (/tmp/app/path+0x4db3a59)
#2 0x7f99beaab560 (/usr/lib/libc.so.6+0x4255f)
#3 0x561dc8c807a4 (/tmp/app/path+0x21387a3)
#4 0x561dcb1036b9 (/tmp/app/path+0x45bb6b8)
#5 0x561dcb1046e3 (/tmp/app/path+0x45bc6e2)
#6 0x561dc99f04fc (/tmp/app/path+0x2ea84fb)
#7 0x561dcbbe43ea (/tmp/app/path+0x509c3e9)
#8 0x561dcbbe922c (/tmp/app/path+0x50a122b)
#9 0x561dcbbe578a (/tmp/app/path+0x509d789)
#10 0x561dcbbf146c (/tmp/app/path+0x50a946b)
#11 0x561dcbbf0c08 (/tmp/app/path+0x50a8c07)
#12 0x561dcbbe922c (/tmp/app/path+0x50a122b)
#13 0x561dcbbe1d7e (/tmp/app/path+0x5099d7d)
#14 0x561dcbbe30cf (/tmp/app/path+0x509b0ce)
#15 0x561dcbc038ee (/tmp/app/path+0x50bb8ed)
#16 0x561dcb8b3ac6 (/tmp/app/path+0x4d6bac5)
#17 0x561dcb8d0139 (/tmp/app/path+0x4d88138)
#18 0x561dcb87fe66 (/tmp/app/path+0x4d37e65)
#19 0x561dcb8d0ba4 (/tmp/app/path+0x4d88ba3)
#20 0x561dcb89d072 (/tmp/app/path+0x4d55071)
#21 0x561dcabb684f (/tmp/app/path+0x406e84e)
#22 0x561dcabb83c2 (/tmp/app/path+0x40703c1)
#23 0x561dcabb3fbe (/tmp/app/path+0x406bfbd)
#24 0x561dc8e01521 (/tmp/app/path+0x22b9520)
#25 0x561dc8e02a18 (/tmp/app/path+0x22baa17)
#26 0x561dc8e0250b (/tmp/app/path+0x22ba50a)
#27 0x561dc8dffb4d (/tmp/app/path+0x22b7b4c)
#28 0x561dc8e00334 (/tmp/app/path+0x22b8333)
#29 0x561dc8b8be5b (/tmp/app/path+0x2043e5a)
#30 0x7f99bea96310 (/usr/lib/libc.so.6+0x2d30f)
  r8: 0000000000000005  r9: 0000561dc80e14c0 r10: 0000000000000020 r11: 0000561dc80e15c0
 r12: 0000000000000000 r13: 0000090402f693a0 r14: 0000090402f693c0 r15: 0000000000000024
  di: 0000561dc7cd610d  si: 0000090402f5d1a1  bp: 00007fff867a9510  bx: 0000561dc7cd610d
  dx: 0000090402f5d1a0  ax: 0000000000000000  cx: 0000000000000074  sp: 00007fff867a9450
  ip: 0000561dc8c487b5 efl: 0000000000010202 cgf: 002b000000000033 erf: 0000000000000004
 trp: 000000000000000e msk: 0000000000000000 cr2: 0000000000000000
[end of stack trace]
```


Discussion / hints:

Fix patch:
diff --git a/shell/browser/electron_permission_manager.cc b/shell/browser/electron_permission_manager.cc
index 6970369b80432..a6235561a1aae 100644
--- a/shell/browser/electron_permission_manager.cc
+++ b/shell/browser/electron_permission_manager.cc
@@ -335,22 +335,32 @@ bool ElectronPermissionManager::CheckDevicePermission(
                    static_cast<blink::PermissionType>(
                        WebContentsPermissionHelper::PermissionType::SERIAL)) {
 #if BUILDFLAG(IS_WIN)
-          if (device->FindStringKey(kDeviceInstanceIdKey) ==
-              granted_device.FindStringKey(kDeviceInstanceIdKey))
+          const auto* instance_id = device->FindStringKey(kDeviceInstanceIdKey);
+          const auto* port_instance_id =
+              granted_device.FindStringKey(kDeviceInstanceIdKey);
+          if (instance_id && port_instance_id &&
+              *instance_id == *port_instance_id)
             return true;
 #else
+          const auto* serial_number =
+              granted_device.FindStringKey(kSerialNumberKey);
+          const auto* port_serial_number =
+              device->FindStringKey(kSerialNumberKey);
           if (device->FindIntKey(kVendorIdKey) !=
                   granted_device.FindIntKey(kVendorIdKey) ||
               device->FindIntKey(kProductIdKey) !=
                   granted_device.FindIntKey(kProductIdKey) ||
-              *device->FindStringKey(kSerialNumberKey) !=
-                  *granted_device.FindStringKey(kSerialNumberKey)) {
+              (serial_number && port_serial_number &&
+               *port_serial_number != *serial_number)) {
             continue;
           }
 
 #if BUILDFLAG(IS_MAC)
-          if (*device->FindStringKey(kUsbDriverKey) !=
-              *granted_device.FindStringKey(kUsbDriverKey)) {
+          const auto* usb_driver_key = device->FindStringKey(kUsbDriverKey);
+          const auto* port_usb_driver_key =
+              granted_device.FindStringKey(kUsbDriverKey);
+          if (usb_driver_key && port_usb_driver_key &&
+              *usb_driver_key != *port_usb_driver_key) {
             continue;
           }
 #endif  // BUILDFLAG(IS_MAC)
diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts
index 4d348bb924c47..0de2068f9590c 100644
--- a/spec-main/api-browser-window-spec.ts
+++ b/spec-main/api-browser-window-spec.ts
@@ -2187,8 +2187,10 @@ describe('BrowserWindow module', () => {
       const [, newOverlayRect] = await geometryChange;
       expect(newOverlayRect.width).to.equal(overlayRect.width + 400);
     };
-    afterEach(closeAllWindows);
-    afterEach(() => { ipcMain.removeAllListeners('geometrychange'); });
+    afterEach(async () => {
+      await closeAllWindows();
+      ipcMain.removeAllListeners('geometrychange');
+    });
     it('creates browser window with hidden title bar', () => {
       const w = new BrowserWindow({
         show: false,
@@ -2266,16 +2268,20 @@ describe('BrowserWindow module', () => {
       // Confirm that maximization only affected the height of the buttons and not the title bar
       expect(overlayRectPostMax.height).to.equal(size);
     };
-    afterEach(closeAllWindows);
-    afterEach(() => { ipcMain.removeAllListeners('geometrychange'); });
+    afterEach(async () => {
+      await closeAllWindows();
+      ipcMain.removeAllListeners('geometrychange');
+    });
     it('sets Window Control Overlay with title bar height of 40', async () => {
       await testWindowsOverlayHeight(40);
     });
   });
 
   ifdescribe(process.platform === 'win32')('BrowserWindow.setTitlebarOverlay', () => {
-    afterEach(closeAllWindows);
-    afterEach(() => { ipcMain.removeAllListeners('geometrychange'); });
+    afterEach(async () => {
+      await closeAllWindows();
+      ipcMain.removeAllListeners('geometrychange');
+    });
 
     it('does not crash when an invalid titleBarStyle was initially set', () => {
       const win = new BrowserWindow({
@@ -2299,10 +2305,13 @@ describe('BrowserWindow module', () => {
     });
 
     it('correctly updates the height of the overlay', async () => {
-      const testOverlay = async (w: BrowserWindow, size: Number) => {
+      const testOverlay = async (w: BrowserWindow, size: Number, firstRun: boolean) => {
         const overlayHTML = path.join(__dirname, 'fixtures', 'pages', 'overlay.html');
-        w.loadFile(overlayHTML);
-        await emittedOnce(ipcMain, 'geometrychange');
+        const overlayReady = emittedOnce(ipcMain, 'geometrychange');
+        await w.loadFile(overlayHTML);
+        if (firstRun) {
+          await overlayReady;
+        }
 
         const overlayEnabled = await w.webContents.executeJavaScript('navigator.windowControlsOverlay.visible');
         expect(overlayEnabled).to.be.true('overlayEnabled');
@@ -2339,13 +2348,13 @@ describe('BrowserWindow module', () => {
         }
       });
 
-      await testOverlay(w, INITIAL_SIZE);
+      await testOverlay(w, INITIAL_SIZE, true);
 
       w.setTitleBarOverlay({
         height: INITIAL_SIZE + 10
       });
 
-      await testOverlay(w, INITIAL_SIZE + 10);
+      await testOverlay(w, INITIAL_SIZE + 10, false);
     });
   });
 
diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts
index ca93d5e0954bd..a69e1b4cc433f 100644
--- a/spec-main/chromium-spec.ts
+++ b/spec-main/chromium-spec.ts
@@ -1678,11 +1678,39 @@ describe('navigator.serial', () => {
   });
 
   it('returns a port when select-serial-port event is defined', async () => {
+    let havePorts = false;
     w.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
-      callback(portList[0].portId);
+      if (portList.length > 0) {
+        havePorts = true;
+        callback(portList[0].portId);
+      } else {
+        callback('');
+      }
     });
     const port = await getPorts();
-    expect(port).to.equal('[object SerialPort]');
+    if (havePorts) {
+      expect(port).to.equal('[object SerialPort]');
+    } else {
+      expect(port).to.equal('NotFoundError: No port selected by the user.');
+    }
+  });
+
+  it('navigator.serial.getPorts() returns values', async () => {
+    let havePorts = false;
+
+    w.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
+      if (portList.length > 0) {
+        havePorts = true;
+        callback(portList[0].portId);
+      } else {
+        callback('');
+      }
+    });
+    await getPorts();
+    if (havePorts) {
+      const grantedPorts = await w.webContents.executeJavaScript('navigator.serial.getPorts()');
+      expect(grantedPorts).to.not.be.empty();
+    }
   });
 });
 
@@ -2005,7 +2033,6 @@ describe('navigator.hid', () => {
       if (details.deviceList.length > 0) {
         details.deviceList.find((device) => {
           if (device.name && device.name !== '' && device.serialNumber && device.serialNumber !== '') {
-            console.log('device is: ', device);
             if (checkForExcludedDevice) {
               const compareDevice = {
                 vendorId: device.vendorId,


Test patch: