{"record":{"id":"f352e4a3bc10aa19","repo":"NationalSecurityAgency/ghidra","slug":"data-and-mask-must-have-same-capacity","errorCode":null,"errorMessage":"data and mask must have same capacity","messagePattern":"data and mask must have same capacity","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java","lineNumber":891,"sourceCode":"\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!ByteBufferUtils.maskedEquals(mask, data, read)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn addr;\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Address findBytes(long snap, AddressRange range, ByteBuffer data, ByteBuffer mask,\n\t\t\tboolean forward, TaskMonitor monitor) {\n\t\t// ProgramDB uses the naive method with some skipping, so here we go....\n\t\t// TODO: This could be made faster by skipping over non-initialized blocks\n\t\t// TODO: DFA method would be complicated by masks....\n\t\tint len = data.capacity();\n\t\tif (mask != null && mask.capacity() != len) {\n\t\t\tthrow new IllegalArgumentException(\"data and mask must have same capacity\");\n\t\t}\n\t\tif (len == 0 ||\n\t\t\trange.getLength() > 0 /*treat length unsigned*/ && range.getLength() < len) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// LATER: Worry about the viewport, too?\n\t\t// This will reduce the search to ranges that have any once-known value at the snap.\n\t\t// NOTE: Potentially costly to pre-compute the set concretely\n\t\tAddressSet known = new AddressSet(\n\t\t\tstateMapSpace.getAddressSetView(Lifespan.at(snap), StatePredicate.IS_KNOWN))\n\t\t\t\t\t.intersect(new AddressSet(range));\n\t\tmonitor.initialize(known.getNumAddresses());\n\t\tfor (AddressRange knownRange : known.getAddressRanges(forward)) {\n\t\t\tAddress found = doFindBytesInRange(snap, knownRange, data, mask, forward, monitor);\n\t\t\tif (found != null) {\n\t\t\t\treturn found;\n\t\t\t}","sourceCodeStart":873,"sourceCodeEnd":909,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java#L873-L909","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure both ByteBuffer objects have the same capacity: allocate mask with ByteBuffer.allocate(data.capacity()) or verify sizes before calling.","Use a helper to construct matched data/mask pairs from a byte pattern and mask string.","If searching without a mask, pass null for the mask parameter instead of an empty or mismatched buffer."],"exampleFix":"// before\nByteBuffer data = ByteBuffer.wrap(patternBytes);\nByteBuffer mask = ByteBuffer.wrap(maskBytes); // different length!\nAddress found = memory.findBytes(snap, range, data, mask, true, monitor);\n\n// after\nassert patternBytes.length == maskBytes.length;\nByteBuffer data = ByteBuffer.wrap(patternBytes);\nByteBuffer mask = ByteBuffer.wrap(maskBytes);\nAddress found = memory.findBytes(snap, range, data, mask, true, monitor);","handlingStrategy":"validation","validationCode":"// Validate buffer capacities before searching\nByteBuffer data = /* pattern */;\nByteBuffer mask = /* mask or null */;\nif (mask != null && mask.capacity() != data.capacity()) {\n    throw new IllegalStateException(\n        \"data capacity (\" + data.capacity() + \") != mask capacity (\" + mask.capacity() + \")\");\n}\nAddress found = memory.findBytes(snap, range, data, mask, true, monitor);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always allocate data and mask buffers with the same capacity.","Pass null for mask when no masking is needed.","Add an assertion: assert mask == null || mask.capacity() == data.capacity()."],"tags":["memory","trace-database","search","byte-buffer","validation","unchecked-exception"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}