# \[Bug report\] BinFHE EvalBinGate(vector) lacks arity/null checks and crashes on malformed vectors

**URL:** https://openfhe.discourse.group/t/bug-report-binfhe-evalbingate-vector-lacks-arity-null-checks-and-crashes-on-malformed-vectors/2356
**Category:** Bug Reports
**Created:** [July 30, 2026, 5:41am UTC](https://openfhe.discourse.group/t/bug-report-binfhe-evalbingate-vector-lacks-arity-null-checks-and-crashes-on-malformed-vectors/2356 "2026-07-30T05:41:16Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![CCYJ](https://avatars.discourse-cdn.com/v4/letter/c/b5ac83/32.png) [@CCYJ](https://openfhe.discourse.group/u/CCYJ)
#### Post date: [July 30, 2026, 5:41am UTC](https://openfhe.discourse.group/t/bug-report-binfhe-evalbingate-vector-lacks-arity-null-checks-and-crashes-on-malformed-vectors/2356/1 "2026-07-30T05:41:17Z")

</div>

Hi OpenFHE team,

I would like to report an input-validation issue in the BinFHE multi-input gate API that can result in undefined behavior and process crashes.

# **Summary**

`BinFHEContext::EvalBinGate(BINGATE, const std::vector<LWECiphertext>&, ...)` forwards the input vector to `BinFHEScheme::EvalBinGate()` without checking:

- that the vector has the exact arity required by the selected gate
- that no element in the vector is null

In particular, for `AND3`, `OR3`, `AND4`, `OR4`, and `MAJORITY`, an empty vector reaches the `ctvector[0]` access before any length validation occurs.

I reproduced three distinct malformed-input behaviors locally under ASan/UBSan on OpenFHE `1.5.1`:

1. `EvalBinGate(AND3, {})` crashes on an empty vector
2. `EvalBinGate(AND3, {validCt1, nullptr, validCt2})` crashes even though the vector length is correct
3. `EvalBinGate(AND3, {validCt1, validCt2})` is accepted and returns a ciphertext instead of rejecting the unsupported arity

# **Environment**

- OpenFHE version: `1.5.1`
- Compiler: `clang 14.0.0`
- Sanitizers: `AddressSanitizer`, `UndefinedBehaviorSanitizer`

# **Public issue check**

As of July 30, 2026, I found a related earlier public null-input report:

- OpenFHE Discourse topic `Boolean Scheme Decrypt Error` (`/t/boolean-scheme-decrypt-error/1704`)
- linked GitHub issue `#907 Add error handling`

That Discourse follow-up generically lists `cc.EvalBinGate()` among APIs that may crash on `nullptr` inputs, so I do not think this report is completely unrelated to the earlier bug family.

However, I did not find an exact public report for the vector overload crashing on:

- empty input vectors
- missing arity checks for `AND3` / `OR3` / `AND4` / `OR4` / `MAJORITY`
- null elements inside the vector overload specifically

I also reviewed the later OpenFHE fixes that appear to be related to the earlier report:

- PR `#939` added explicit `nullptr` checks to the two-ciphertext `EvalBinGate(ct1, ct2)` path, but in the vector overload it only added a `params == nullptr` check
- PR `#1078`, which closed `#907`, added null checks only to `LWEEncryptionScheme::Decrypt()`

So this looks more like a related but still-unfixed vector-overload variant.

# **Minimal reproduction**

The reproducer below passes an empty ciphertext vector to the multi-input gate overload:

```cpp
#include "binfhecontext.h"

using namespace lbcrypto;

int main() {
    BinFHEContext cc;
    cc.GenerateBinFHEContext(TOY);

    auto sk = cc.KeyGen();
    cc.BTKeyGen(sk);

    std::vector<LWECiphertext> cts;
    auto out = cc.EvalBinGate(AND3, cts);
    (void)out;

    return 0;
}

```

To isolate null-element handling from arity validation, I also reproduced the issue with a correctly sized `AND3` vector whose second element was null:

```cpp
auto ct0 = cc.Encrypt(sk, 0);
auto ct1 = cc.Encrypt(sk, 1);
std::vector<LWECiphertext> cts = {ct1, LWECiphertext{}, ct0};
auto out = cc.EvalBinGate(AND3, cts);

```

To isolate the arity-validation problem from null handling, I also reproduced that `AND3` accepts a two-element vector whose elements are both valid ciphertexts:

```cpp
auto ct0 = cc.Encrypt(sk, 0);
auto ct1 = cc.Encrypt(sk, 1);
std::vector<LWECiphertext> cts = {ct1, ct0};
auto out = cc.EvalBinGate(AND3, cts);

```

# **Expected behavior**

The vector overload should reject malformed inputs with an OpenFHE exception before performing any vector indexing or ciphertext dereference. At minimum, I would expect:

- exact arity validation for `AND3`, `OR3`, `AND4`, `OR4`, and `MAJORITY`
- null-element validation

`CMUX` already has a size-3 check in the current implementation, so I am not including it in the missing-arity portion of this report.

# **Actual behavior**

For the empty-vector case, UBSan reports invalid access through `std::vector::operator[]`, followed by a crash in `BinFHEScheme::EvalBinGate()`:

```plaintext
runtime error: reference binding to null pointer of type 'const std::shared_ptr<lbcrypto::LWECiphertextImpl>'
    #0 ... std::vector<...>::operator[](...) .../stl_vector.h:1143
    #1 ... lbcrypto::BinFHEScheme::EvalBinGate(...) .../binfhe-base-scheme.cpp:139

```

For the correct-arity null-element case, UBSan reports a null member call and ASan ends with a null dereference:

```plaintext
runtime error: member call on null pointer of type 'lbcrypto::LWECiphertextImpl'
    #0 ... lbcrypto::BinFHEScheme::EvalBinGate(...) .../binfhe-base-scheme.cpp:141
...
==...==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
    #0 ... lbcrypto::LWECiphertextImpl::GetModulus() const .../lwe-ciphertext.h:86
    #1 ... lbcrypto::BinFHEScheme::EvalBinGate(...) .../binfhe-base-scheme.cpp:141

```

For the wrong-arity case, no exception is thrown: `EvalBinGate(AND3, {validCt1, validCt2})` completes and returns a non-null ciphertext instead of rejecting the unsupported input length.

# **Cause analysis**

The vector overload in `BinFHEContext` does not perform any validation:

```cpp
LWECiphertext BinFHEContext::EvalBinGate(const BINGATE gate,
                                         const std::vector<LWECiphertext>& ctvector,
                                         bool extended) const {
    return m_binfhescheme->EvalBinGate(m_params, gate, m_BTKey, ctvector, extended);
}

```

Inside `BinFHEScheme::EvalBinGate()`, in the branch handling `AND3`, `OR3`, `AND4`, `OR4`, and `MAJORITY`, the implementation does not validate the vector length or whether its elements are non-null before the first `ctvector[0]` dereference:

```cpp
auto ct = (Q == ctvector[0]->GetModulus())
              ? LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ctvector[0])
              : std::make_shared<LWECiphertextImpl>(*ctvector[0]);
...
auto p = ctvector[0]->GetptModulus();

```

So an empty vector is unsafe, and any call that reaches this line with a null first element is unsafe:

- `ctvector.empty()`
- `ctvector[0] == nullptr`

The same code path then iterates over the remaining elements and dereferences `ctvector[i]` in the addition loop without checking whether any later element is null:

```cpp
for (uint32_t i = 1; i < length; ++i) {
    LWEscheme->EvalAddEq(ct, (Q == ctvector[i]->GetModulus()) ? LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ctvector[i]) : ctvector[i]);
}

```

So a correctly sized vector such as `{validCt1, nullptr, validCt2}` still reaches an unsafe dereference.

The same function also does not appear to enforce the documented arity for `AND3`, `OR3`, `AND4`, `OR4`, or `MAJORITY`. A two-element `AND3` vector with both elements valid is accepted and processed instead of being rejected up front.

By contrast, `CMUX` does already check `length != 3` before proceeding, so the missing-arity part of this report applies to the other multi-input gates, not to `CMUX`.

# **Relevant source locations**

- `src/binfhe/lib/binfhecontext.cpp`
  - `BinFHEContext::EvalBinGate(vector overload)`: lines 283-285
  - GitHub link: [https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/binfhecontext.cpp#L283-L285](https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/binfhecontext.cpp#L283-L285)

- `src/binfhe/lib/binfhe-base-scheme.cpp`
  - `BinFHEScheme::EvalBinGate(vector overload)`: lines 121-173
  - First unsafe dereference: line 139
  - Null later-element dereference in the accumulation loop: line 141
  - GitHub link: [https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/binfhe-base-scheme.cpp#L121-L173](https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/binfhe-base-scheme.cpp#L121-L173)

- `src/binfhe/include/binfhecontext.h`
  - documented vector-gate API: lines 290-297
  - GitHub link: [https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/include/binfhecontext.h#L290-L297](https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/include/binfhecontext.h#L290-L297)

- `src/binfhe/include/binfhe-base-scheme.h`
  - documented supported vector gates: lines 96-107
  - GitHub link: [https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/include/binfhe-base-scheme.h#L96-L107](https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/include/binfhe-base-scheme.h#L96-L107)

# **Suggested direction**

It looks reasonable to validate, before any dereference:

- required vector size for `AND3`, `OR3`, `AND4`, `OR4`, and `MAJORITY`
- `ctvector[i] != nullptr` for every element

It might also be worth keeping the vector overload consistent with the two-input overload, which already checks for null ciphertexts explicitly after the earlier null-input fixes.

# **Question**

Is malformed multi-input gate input supposed to be rejected explicitly at the `EvalBinGate(vector)` boundary, or is that currently treated as an unchecked caller-side precondition?

Reported by Jiang Chao, Beijing University of Posts and Telecommunications

---

<div class="post-metadata">

### Author: ![ypolyakov](https://yyz1.discourse-cdn.com/flex031/user_avatar/openfhe.discourse.group/ypolyakov/32/47_2.png) [@ypolyakov](https://openfhe.discourse.group/u/ypolyakov)
#### Post date: [August 3, 2026, 6:52pm UTC](https://openfhe.discourse.group/t/bug-report-binfhe-evalbingate-vector-lacks-arity-null-checks-and-crashes-on-malformed-vectors/2356/2 "2026-08-03T18:52:30Z")

</div>

Again, our focus is on the honest-but-curious threat model. I’ve added this item to issue [[Bug report] BinFHE EvalBinGate(vector) lacks arity/null checks and crashes on malformed vectors](https://openfhe.discourse.group/t/bug-report-binfhe-evalbingate-vector-lacks-arity-null-checks-and-crashes-on-malformed-vectors/2356) as it seems to be easy to fix.
