QSPICE64.exe crashes with an unhandled access violation when a user-defined .func contains a logical OR operator (| or ||) where both operands depend on function arguments. Critical (simulator crash, no error message, 0-byte output file)

Steps to Reproduce

Save the following as test.cir and run:

V1 a 0 PULSE(0 5 1n 1n 1n 1u 2u)

V2 b 0 PULSE(0 5 1n 1n 1n 0.5u 2u)

Rz z 0 1meg

.func F(x,y) {if(x>1|y>1,9,0)}

E1 z 0 VALUE {F(V(a),V(b))}

.tran 0 5u

.end

Where | Does NOT Crash

  • Inline in B / E / G VALUE {…} expressions
  • Inside a .func with no parameters
  • As an argument passed into a .func (not in the .func body)
  • In subcircuit-level expressions
  1. QSPICE version and build:
  • QUX.exe Build: Oct. 2 2026
  • QSPICE64.exe Build: Sep. 11 2026
  • QSPICE80.exe Build: Sep. 11 2026
  • QPOST.exe Build: Sep. 9 2026
  1. Operating system

Windows 11, version 25H2 for x64-based Systems (KB5124008) (26200.9445)

1 Like

Hello VideanuCatalin

I didn’t make a netlist and I simply copy-pasted the contents of the netlist inside a text box in QSPICE and, it crashed. I removed the .end, same. With | or ||, it crashes in E/G/B sources. ^, ^^, &, && work. However, I’m on CachyOS and Wine staging threw this on me:

Unhandled exception: page fault on read access to 0x00000000 in wow64 32-bit code (0x00494507).
Register dump:
 CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:002b
 EIP:00494507 ESP:0026ac60 EBP:0026aca0 EFLAGS:00010246(  R- --  I  Z- -P- )
 EAX:00000002 EBX:00000000 ECX:0026aca0 EDX:002ca068
 ESI:002cb450 EDI:00000000
Stack dump:
0x0026ac60:  00000000 00000000 002cb540 00000010
0x0026ac70:  00266c30 00000000 0026ac50 0026ace0
0x0026ac80:  002ca088 0026aca0 002cb540 004943ca
0x0026ac90:  0026ace0 004943a8 0026accc 002cb360
0x0026aca0:  002cb2d0 002cb330 0049432a 00000002
0x0026acb0:  00494379 0026ad78 00000000 002cb362
Backtrace:
=>0 0x00494507 in qspice64 (+0x94507) (0x0026aca0)
  1 0x002cb330 (0x002cb2d0)
0x00494507 qspice64+0x94507: movb (%edi), %cl

It goes a bit above my head so, I asked the AI and I got what’s below as a response (the one point I find interesting is that about “delimiters/absolute-value collision” though, I am not implying anything but, maybe someone else spots something helpful/useful):


What the Crash Dump Reveals

  1. Crash Instruction (movb (%edi), %cl with EDI = 0x00000000):
    The program crashed on an x86 string/byte read (mov cl, byte ptr [edi]) attempting to dereference a NULL pointer. In MSVC-compiled binaries, this pattern is the classic inline expansion of char c = *p; or strlen(p).

  2. The Call Chain (0x00494379 $\to$ 0x0049432a $\to$ 0x004943a8 $\to$ 0x004943ca $\to$ 0x00494507):
    All return addresses on the stack sit within a tight ~300-byte span. This corresponds to the mutually recursive precedence levels of QSPICE’s recursive-descent expression parser (e.g., parse_or $\to$ parse_xor $\to$ parse_and $\to$ parse_relational).

  3. Heap Pointers in the Stack Frame (0x002cb360 and 0x002cb362):
    The difference between these two string buffer pointers on the stack is exactly 2 bytes, matching the two-character length of ||. The lexer successfully scanned across the operator, but the pointer returned or stored for the AST/node generator became NULL.

  4. Operator Code (EAX = 0x00000002 / [ebp+8] = 0x00000002):
    2 is passed directly as the operator/token ID. In logical operator enumerations, 1 is commonly AND, 2 is OR, and 3 is XOR.


Root-Cause Mechanisms

  • Bypassed vs. Entered Precedence Level:
    In standard operator precedence, OR is at the outermost (lowest precedence) level of binary logical expressions:
    $$\text{OR} \longrightarrow \text{XOR} \longrightarrow \text{AND} \longrightarrow \text{Relational}$$
    When an expression contains only &/&& or ^/^^, parse_or() delegates immediately down the chain and never enters its own while (tok == OP_OR) loop. That loop is only entered when the operator is | or ||. Any bug inside that specific handler remains dormant until a pipe operator is parsed.

  • Missing Return / NULL Operator Metadata:
    Inside the OP_OR branch of the parser or AST constructor:

    • A helper function advancing past |/|| may lack an explicit return p; on that branch, falling through to return NULL;. The caller immediately attempts to peek at the next character (*p) and page-faults on 0x0.
    • Alternatively, an internal operator-symbol table or code-generation dispatch table has NULL at index 2 (OP_OR). Passing this NULL string to an internal formatter or parser causes an immediate access violation (MSVC’s CRT does not guard against NULL in %s / strlen).
  • Delimiters / Absolute-Value Collision:
    If the scanner contains an incomplete or legacy rule treating | as a paired mathematical absolute-value bracket (|x|), encountering x > 1 | y > 1 leaves the absolute-value token unclosed. When it scans to the end of the sub-expression without finding a matching |, it returns NULL without a guard check.


Workarounds

To achieve the exact same logic without triggering the pipe parser bug:

  1. Arithmetic OR (cleanest in SPICE):
    Boolean relational operators evaluate to 1.0 (true) or 0.0 (false). Adding them behaves as a logical OR:

    .func F(x,y) {(x > 1) + (y > 1) > 0 ? 9 : 0}
    

    or with if():

    .func F(x,y) {if((x > 1) + (y > 1), 9, 0)}
    
  2. De Morgan’s Law (using working & and !):
    $$A \lor B \iff \neg(\neg A \land \neg B)$$

    .func F(x,y) {if(!((x <= 1) & (y <= 1)), 9, 0)}
    
  3. Nested if / Ternary (native short-circuiting):

    .func F(x,y) {if(x > 1, 9, if(y > 1, 9, 0))}
    

@VideanuCatalin

I reviewed your post and sent an email to Mike to report this bug.
Update Qspice to verify if you still encounter problem.
Bug is reported through email in Qspice > Help > About Qspice.
10/05/2026 Corrected in error in the .func compiler.