NationalSecurityAgency/ghidra · error · IllegalArgumentException

data and mask must have same capacity

Error message

data and mask must have same capacity

What it means

Thrown by DBTraceMemorySpace.findBytes() when the data ByteBuffer and the mask ByteBuffer passed to the search have different capacities. The method uses data.capacity() as the expected length and checks mask.capacity() != len when mask is non-null. An IllegalArgumentException (unchecked) signals a caller contract violation: both buffers must have identical capacity for the masked byte search to work.

Source

Thrown at Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java:891

				continue;
			}
			if (!ByteBufferUtils.maskedEquals(mask, data, read)) {
				continue;
			}
			return addr;
		}
		return null;
	}

	@Override
	public Address findBytes(long snap, AddressRange range, ByteBuffer data, ByteBuffer mask,
			boolean forward, TaskMonitor monitor) {
		// ProgramDB uses the naive method with some skipping, so here we go....
		// TODO: This could be made faster by skipping over non-initialized blocks
		// TODO: DFA method would be complicated by masks....
		int len = data.capacity();
		if (mask != null && mask.capacity() != len) {
			throw new IllegalArgumentException("data and mask must have same capacity");
		}
		if (len == 0 ||
			range.getLength() > 0 /*treat length unsigned*/ && range.getLength() < len) {
			return null;
		}

		// LATER: Worry about the viewport, too?
		// This will reduce the search to ranges that have any once-known value at the snap.
		// NOTE: Potentially costly to pre-compute the set concretely
		AddressSet known = new AddressSet(
			stateMapSpace.getAddressSetView(Lifespan.at(snap), StatePredicate.IS_KNOWN))
					.intersect(new AddressSet(range));
		monitor.initialize(known.getNumAddresses());
		for (AddressRange knownRange : known.getAddressRanges(forward)) {
			Address found = doFindBytesInRange(snap, knownRange, data, mask, forward, monitor);
			if (found != null) {
				return found;
			}

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Ensure both ByteBuffer objects have the same capacity: allocate mask with ByteBuffer.allocate(data.capacity()) or verify sizes before calling.
  2. Use a helper to construct matched data/mask pairs from a byte pattern and mask string.
  3. If searching without a mask, pass null for the mask parameter instead of an empty or mismatched buffer.

Example fix

// before
ByteBuffer data = ByteBuffer.wrap(patternBytes);
ByteBuffer mask = ByteBuffer.wrap(maskBytes); // different length!
Address found = memory.findBytes(snap, range, data, mask, true, monitor);

// after
assert patternBytes.length == maskBytes.length;
ByteBuffer data = ByteBuffer.wrap(patternBytes);
ByteBuffer mask = ByteBuffer.wrap(maskBytes);
Address found = memory.findBytes(snap, range, data, mask, true, monitor);
Defensive patterns

Strategy: validation

Validate before calling

// Validate buffer capacities before searching
ByteBuffer data = /* pattern */;
ByteBuffer mask = /* mask or null */;
if (mask != null && mask.capacity() != data.capacity()) {
    throw new IllegalStateException(
        "data capacity (" + data.capacity() + ") != mask capacity (" + mask.capacity() + ")");
}
Address found = memory.findBytes(snap, range, data, mask, true, monitor);

Prevention

When it happens

Trigger: Calling findBytes(snap, range, data, mask, forward, monitor) where data.capacity() != mask.capacity(). For example, passing a 4-byte pattern buffer with a 1-byte mask, or vice versa. The check fires immediately before the search begins.

Common situations: Constructing search patterns programmatically where the data and mask buffers are allocated independently with different sizes. Copying a mask from one search context to another with a different data length. Off-by-one in buffer allocation.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/f352e4a3bc10aa19. Report an issue: GitHub.