- Split embedded OpenCL kernel_source into kernel_parts[] array so no single string literal exceeds MSVC's 65535-byte limit. clCreateProgramWithSource now receives multiple source strings. - Added batch-inversion kernels (field_inv, affine_add, jac_to_affine) using per-workgroup Montgomery's trick with __local memory. - OpenCL BIP352 benchmark scaffold and kernel stubs. - Source graph kit for indexed codebase exploration. - Assorted doc, benchmark, and audit report updates.
894 lines
34 KiB
Python
894 lines
34 KiB
Python
#!/usr/bin/env python3
|
|
"""Fix all 211 code-scanning alerts across 13 files."""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
BASE = Path('/home/shrek/Secp256K1/Secp256K1fast/libs/UltrafastSecp256k1')
|
|
|
|
|
|
def read(path):
|
|
return (BASE / path).read_text().splitlines(keepends=True)
|
|
|
|
|
|
def save(path, lines):
|
|
(BASE / path).write_text(''.join(lines))
|
|
|
|
|
|
# ============================================================================
|
|
# Algorithmic helpers
|
|
# ============================================================================
|
|
|
|
def add_braces(lines, alert_lines_1based, tag=''):
|
|
"""Add { } around single-statement bodies. Process bottom-to-top."""
|
|
fixed = 0
|
|
for lnum in sorted(alert_lines_1based, reverse=True):
|
|
idx = lnum - 1
|
|
if idx >= len(lines):
|
|
print(f' SKIP {tag}L{lnum}: out of range ({len(lines)} lines)')
|
|
continue
|
|
line = lines[idx]
|
|
# Get indentation of the controlling statement
|
|
indent = len(line) - len(line.lstrip())
|
|
indent_str = line[:indent]
|
|
stripped = line.rstrip('\n\r').rstrip()
|
|
|
|
# Skip if already has brace at end
|
|
if stripped.endswith('{'):
|
|
print(f' SKIP {tag}L{lnum}: already has {{')
|
|
continue
|
|
|
|
# Find next non-empty line (the body)
|
|
body_idx = idx + 1
|
|
while body_idx < len(lines) and lines[body_idx].strip() == '':
|
|
body_idx += 1
|
|
|
|
if body_idx >= len(lines):
|
|
print(f' SKIP {tag}L{lnum}: no body line found')
|
|
continue
|
|
|
|
body_line_stripped = lines[body_idx].lstrip()
|
|
# Skip if body already starts with {
|
|
if body_line_stripped.startswith('{'):
|
|
print(f' SKIP {tag}L{lnum}: body already has {{')
|
|
continue
|
|
|
|
# Apply fix
|
|
lines[idx] = stripped + ' {\n'
|
|
lines.insert(body_idx + 1, indent_str + '}\n')
|
|
fixed += 1
|
|
|
|
print(f' -> {tag}braces fixed: {fixed}')
|
|
return lines
|
|
|
|
|
|
def add_const_to_lines(lines, alert_lines_1based, tag=''):
|
|
"""Prepend const to variable declarations, handling range-for loops."""
|
|
fixed = 0
|
|
for lnum in sorted(alert_lines_1based, reverse=True):
|
|
idx = lnum - 1
|
|
if idx >= len(lines):
|
|
continue
|
|
line = lines[idx]
|
|
stripped = line.lstrip()
|
|
leading = line[:len(line) - len(stripped)]
|
|
|
|
if stripped.startswith('const '):
|
|
print(f' SKIP {tag}L{lnum}: already const')
|
|
continue
|
|
|
|
# Range-based for loop: for (TYPE var : container) -> for (const TYPE var : container)
|
|
if stripped.startswith('for (') and ':' in stripped:
|
|
# Match: for (TYPE var : ...
|
|
m = re.match(r'(for \()(\w[^:]+: .+)', stripped)
|
|
if m:
|
|
lines[idx] = leading + m.group(1) + 'const ' + m.group(2)
|
|
fixed += 1
|
|
continue
|
|
|
|
# Regular declaration
|
|
lines[idx] = leading + 'const ' + stripped
|
|
fixed += 1
|
|
|
|
print(f' -> {tag}const fixed: {fixed}')
|
|
return lines
|
|
|
|
|
|
# ============================================================================
|
|
# File: include/ufsecp/ufsecp_impl.cpp
|
|
# ============================================================================
|
|
|
|
def fix_ufsecp_impl():
|
|
path = 'include/ufsecp/ufsecp_impl.cpp'
|
|
print(f'\n=== {path} ===')
|
|
lines = read(path)
|
|
|
|
# --- readability-braces-around-statements (59 alerts) ---
|
|
brace_lines = [
|
|
1242, 1245, 1248, 1260, 1274, 1277, 1281, 1294, 1297,
|
|
1300, 1314, 1318, 1322, 1340, 1343, 1345, 1355, 1368,
|
|
1412, 1415, 1431, 1435, 1438, 1457, 1462, 1477, 1481,
|
|
1486, 1514, 1516, 1519, 1522, 1525, 1542, 1545, 1549,
|
|
1567, 1577, 1594, 1691, 1695, 1699, 1701, 1749, 1753,
|
|
1787, 1801, 1831, 1834, 1844, 1856, 1974, 2047, 2068,
|
|
2071, 2076, 2138, 2832, 2834,
|
|
]
|
|
lines = add_braces(lines, brace_lines, 'ufsecp_impl/')
|
|
|
|
# --- misc-const-correctness ---
|
|
const_lines = [1366, 1855, 1905, 2075, 3147, 3167, 3172]
|
|
lines = add_const_to_lines(lines, const_lines, 'ufsecp_impl/')
|
|
|
|
# --- modernize-use-auto ---
|
|
# L1573: uint32_t nk = static_cast<uint32_t>(...) -> auto nk = ...
|
|
# L1846: uint32_t cc32 = static_cast<uint32_t>(...) -> auto cc32 = ...
|
|
for lnum in [1573, 1846]:
|
|
idx = lnum - 1
|
|
line = lines[idx]
|
|
m = re.match(r'(\s*)uint32_t (\w+) = (static_cast<uint32_t>\(.+)', line)
|
|
if m:
|
|
lines[idx] = f'{m.group(1)}auto {m.group(2)} = {m.group(3)}'
|
|
print(f' AUTO: L{lnum}')
|
|
|
|
# --- cppcoreguidelines-init-variables ---
|
|
# L1655: uint32_t nk; -> uint32_t nk = 0;
|
|
idx = 1655 - 1
|
|
if ' uint32_t nk;' in lines[idx]:
|
|
lines[idx] = lines[idx].replace('uint32_t nk;', 'uint32_t nk = 0;')
|
|
print(' INIT: L1655')
|
|
|
|
# L1706: { uint32_t nk; -> { uint32_t nk = 0;
|
|
idx = 1706 - 1
|
|
if 'uint32_t nk;' in lines[idx]:
|
|
lines[idx] = lines[idx].replace('uint32_t nk;', 'uint32_t nk = 0;')
|
|
print(' INIT: L1706')
|
|
|
|
# L1761: same pattern
|
|
idx = 1761 - 1
|
|
if 'uint32_t nk;' in lines[idx]:
|
|
lines[idx] = lines[idx].replace('uint32_t nk;', 'uint32_t nk = 0;')
|
|
print(' INIT: L1761')
|
|
|
|
# --- bugprone-implicit-widening-of-multiplication-result ---
|
|
# L1578: keyagg_out + 38 + i * 32 -> keyagg_out + 38 + static_cast<size_t>(i) * 32
|
|
idx = 1578 - 1
|
|
if 'i * 32' in lines[idx] and 'static_cast<size_t>(i)' not in lines[idx]:
|
|
lines[idx] = lines[idx].replace(
|
|
'keyagg_out + 38 + i * 32',
|
|
'keyagg_out + 38 + static_cast<size_t>(i) * 32'
|
|
)
|
|
print(' WIDENING: L1578')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/src/bip39.cpp
|
|
# ============================================================================
|
|
|
|
def fix_bip39():
|
|
path = 'cpu/src/bip39.cpp'
|
|
print(f'\n=== {path} ===')
|
|
lines = read(path)
|
|
|
|
brace_lines = [49, 50, 93, 110, 117, 138, 140, 150, 171, 196, 200, 223,
|
|
246, 269, 273]
|
|
lines = add_braces(lines, brace_lines, 'bip39/')
|
|
|
|
const_lines = [33, 46, 47, 97, 126, 127, 128, 129, 136, 145,
|
|
182, 183, 184, 185, 191, 193, 194, 199,
|
|
255, 256, 257, 258, 264, 266, 267, 272]
|
|
lines = add_const_to_lines(lines, const_lines, 'bip39/')
|
|
|
|
# --- cppcoreguidelines-init-variables ---
|
|
# L137: some variable, need to find it
|
|
idx = 137 - 1
|
|
line = lines[idx]
|
|
# Pattern: TYPE var; (uninitialized) - add = 0 or = {} or = nullptr
|
|
m = re.match(r'(\s*)((?:int|uint\w*|size_t|bool|char|float|double)\s+\w+);(\s*(?://.*)?)\n', line)
|
|
if m:
|
|
type_and_var = m.group(2).rstrip()
|
|
# Determine default value
|
|
if 'bool' in type_and_var:
|
|
default = 'false'
|
|
elif 'float' in type_and_var or 'double' in type_and_var:
|
|
default = '0.0'
|
|
elif 'char*' in type_and_var or 'uint8_t*' in type_and_var:
|
|
default = 'nullptr'
|
|
else:
|
|
default = '0'
|
|
lines[idx] = f'{m.group(1)}{type_and_var} = {default};{m.group(3)}\n'
|
|
print(f' INIT: L137 -> added = {default}')
|
|
else:
|
|
print(f' INIT_SKIP: L137 pattern not matched: {repr(line[:60])}')
|
|
|
|
# --- modernize-use-auto ---
|
|
# L191 and L264: iterator/auto type replacement
|
|
for lnum in [191, 264]:
|
|
idx = lnum - 1
|
|
line = lines[idx]
|
|
# Pattern: SomeType::iterator it = or std::vector<...>::iterator it =
|
|
m = re.match(r'(\s*)(\w[\w:<>, *]+::iterator)(\s+\w+\s*=.+)', line)
|
|
if m:
|
|
lines[idx] = f'{m.group(1)}auto{m.group(3)}'
|
|
print(f' AUTO: L{lnum}')
|
|
else:
|
|
# Try: SomeType it = container.begin()
|
|
m2 = re.match(r'(\s*)(\w[\w:<>, *]+\*?)(\s+\w+\s*=\s*\w.+\.begin\(\).+)', line)
|
|
if m2:
|
|
lines[idx] = f'{m2.group(1)}auto{m2.group(3)}'
|
|
print(f' AUTO: L{lnum}')
|
|
else:
|
|
print(f' AUTO_SKIP: L{lnum}: {repr(line[:60])}')
|
|
|
|
# --- cert-err33-c (unchecked fclose return) ---
|
|
# L34: std::fclose(f); -> (void)std::fclose(f);
|
|
idx = 34 - 1
|
|
line = lines[idx]
|
|
if 'std::fclose' in line and '(void)' not in line:
|
|
lines[idx] = line.replace('std::fclose', '(void)std::fclose')
|
|
print(' ERR33: L34 fclose')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/src/zk.cpp
|
|
# ============================================================================
|
|
|
|
def fix_zk():
|
|
path = 'cpu/src/zk.cpp'
|
|
print(f'\n=== {path} ===')
|
|
lines = read(path)
|
|
|
|
brace_lines = [45, 68, 381, 415, 423, 481, 503, 610, 615, 619, 623,
|
|
664, 668, 675, 686, 688, 720, 785]
|
|
lines = add_braces(lines, brace_lines, 'zk/')
|
|
|
|
const_lines = [359, 363, 446, 448, 500, 642, 661]
|
|
lines = add_const_to_lines(lines, const_lines, 'zk/')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/src/message_signing.cpp
|
|
# ============================================================================
|
|
|
|
def fix_message_signing():
|
|
path = 'cpu/src/message_signing.cpp'
|
|
print(f'\n=== {path} ===')
|
|
lines = read(path)
|
|
|
|
brace_lines = [30, 35]
|
|
lines = add_braces(lines, brace_lines, 'msg_signing/')
|
|
|
|
const_lines = [65, 152, 153, 154, 155, 159, 193, 196]
|
|
lines = add_const_to_lines(lines, const_lines, 'msg_signing/')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/src/eth_signing.cpp
|
|
# ============================================================================
|
|
|
|
def fix_eth_signing():
|
|
path = 'cpu/src/eth_signing.cpp'
|
|
print(f'\n=== {path} ===')
|
|
lines = read(path)
|
|
|
|
# --- misc-unused-using-decls: L16 'using fast::Point;' ---
|
|
idx = 16 - 1
|
|
if 'using fast::Point' in lines[idx]:
|
|
lines[idx] = '' # Remove the line (keep blank to preserve line numbers)
|
|
# Actually remove the line entirely
|
|
lines[idx] = '\n'
|
|
# Better: just delete and shift
|
|
del lines[idx]
|
|
# Now const_lines will shift by -1
|
|
print(' UNUSED-USING: L16 removed')
|
|
# After removal, adjust const lines
|
|
const_lines = [95, 96] # shifted from [96, 97]
|
|
else:
|
|
print(f' UNUSED-USING SKIP: L16: {repr(lines[idx][:50])}')
|
|
const_lines = [96, 97]
|
|
|
|
lines = add_const_to_lines(lines, const_lines, 'eth_signing/')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/src/address.cpp
|
|
# ============================================================================
|
|
|
|
def fix_address():
|
|
path = 'cpu/src/address.cpp'
|
|
print(f'\n=== {path} ===')
|
|
lines = read(path)
|
|
|
|
# L516: for (char c : prefix) -> for (const char c : prefix)
|
|
# L527: std::uint8_t version_byte = ... -> const std::uint8_t version_byte = ...
|
|
# L527: also modernize-use-auto -> auto version_byte = ...
|
|
const_lines = [516, 527]
|
|
lines = add_const_to_lines(lines, const_lines, 'address/')
|
|
|
|
# L527: modernize-use-auto: const std::uint8_t version_byte = static_cast<...>
|
|
# -> const auto version_byte = static_cast<...>
|
|
# This is handled by add_const adding 'const', but we also need to change the type
|
|
# Actually the modernize-use-auto wants: 'auto version_byte = static_cast<std::uint8_t>(...)'
|
|
# And const-correctness wants: 'const ... version_byte = ...'
|
|
# Combined: 'const auto version_byte = static_cast<std::uint8_t>(...)'
|
|
# Let's check what add_const_to_lines did for L527:
|
|
# Line 527 was: std::uint8_t version_byte = static_cast<std::uint8_t>(type << 3);
|
|
# After add_const: const std::uint8_t version_byte = ...
|
|
# But we also want to replace std::uint8_t with auto for modernize-use-auto:
|
|
# Find current state of L527 (0-indexed: 526, but const_lines processed in reverse,
|
|
# so L516 was processed first (higher reverse order), then L527)
|
|
# Actually both were processed with const_lines = [516, 527], processed in reverse: 527, 516
|
|
# After const processing, L527 has 'const std::uint8_t version_byte = ...'
|
|
# Now apply modernize-use-auto: replace 'const std::uint8_t' with 'const auto'
|
|
idx = 527 - 1
|
|
if idx < len(lines):
|
|
line = lines[idx]
|
|
if 'const std::uint8_t version_byte' in line:
|
|
lines[idx] = line.replace('const std::uint8_t version_byte',
|
|
'const auto version_byte')
|
|
print(' AUTO: L527')
|
|
elif 'const auto version_byte' in line:
|
|
print(' AUTO: L527 already auto')
|
|
else:
|
|
print(f' AUTO_SKIP: L527: {repr(line[:60])}')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/src/wallet.cpp
|
|
# ============================================================================
|
|
|
|
def fix_wallet():
|
|
path = 'cpu/src/wallet.cpp'
|
|
print(f'\n=== {path} ===')
|
|
lines = read(path)
|
|
|
|
# L150, L171: bugprone-misplaced-widening-cast
|
|
# Pattern: static_cast<std::uint64_t>(27 + rsig.recid)
|
|
# Fix: static_cast<std::uint64_t>(27) + static_cast<std::uint64_t>(rsig.recid)
|
|
for lnum in [150, 171]:
|
|
idx = lnum - 1
|
|
if idx >= len(lines):
|
|
continue
|
|
line = lines[idx]
|
|
if 'static_cast<std::uint64_t>(27 + rsig.recid)' in line:
|
|
lines[idx] = line.replace(
|
|
'static_cast<std::uint64_t>(27 + rsig.recid)',
|
|
'static_cast<std::uint64_t>(27) + static_cast<std::uint64_t>(rsig.recid)'
|
|
)
|
|
print(f' WIDEN: L{lnum}')
|
|
else:
|
|
print(f' WIDEN_SKIP: L{lnum}: {repr(line[:60])}')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/src/coin_address.cpp
|
|
# ============================================================================
|
|
|
|
def fix_coin_address():
|
|
path = 'cpu/src/coin_address.cpp'
|
|
print(f'\n=== {path} ===')
|
|
lines = read(path)
|
|
|
|
# L170: std::string prefix = testnet ? ... -> const std::string prefix = ...
|
|
const_lines = [170]
|
|
lines = add_const_to_lines(lines, const_lines, 'coin_address/')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/tests/test_bip39.cpp
|
|
# ============================================================================
|
|
|
|
# Helper function for replacing sscanf with strtoul in hex_to_bytes
|
|
HEX_TO_BYTES_SSCANF_BIP39 = '''\
|
|
static void hex_to_bytes(const char* hex, uint8_t* out, size_t len) {
|
|
for (size_t i = 0; i < len; ++i) {
|
|
unsigned int byte = 0;
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
#endif
|
|
std::sscanf(hex + 2 * i, "%02x", &byte);
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
out[i] = static_cast<uint8_t>(byte);
|
|
}
|
|
}'''
|
|
|
|
HEX_TO_BYTES_STRTOUL_BIP39 = '''\
|
|
static void hex_to_bytes(const char* hex, uint8_t* out, size_t len) {
|
|
for (size_t i = 0; i < len; ++i) {
|
|
char pair[3] = { hex[2 * i], hex[2 * i + 1], '\\0' };
|
|
char* endptr = nullptr;
|
|
const unsigned long val = std::strtoul(pair, &endptr, 16);
|
|
out[i] = (endptr == pair + 2) ? static_cast<uint8_t>(val) : 0;
|
|
}
|
|
}'''
|
|
|
|
BYTES_TO_HEX_OLD = '''\
|
|
static std::string bytes_to_hex(const uint8_t* data, size_t len) {
|
|
std::string result;
|
|
result.reserve(len * 2);
|
|
for (size_t i = 0; i < len; ++i) {
|
|
char buf[3];
|
|
std::snprintf(buf, sizeof(buf), "%02x", data[i]);
|
|
result += buf;
|
|
}
|
|
return result;
|
|
}'''
|
|
|
|
BYTES_TO_HEX_NEW = '''\
|
|
static std::string bytes_to_hex(const uint8_t* data, size_t len) {
|
|
std::string result;
|
|
result.reserve(len * 2);
|
|
for (size_t i = 0; i < len; ++i) {
|
|
char buf[3];
|
|
(void)std::snprintf(buf, sizeof(buf), "%02x", data[i]);
|
|
result += buf;
|
|
}
|
|
return result;
|
|
}'''
|
|
|
|
|
|
def fix_test_bip39():
|
|
path = 'cpu/tests/test_bip39.cpp'
|
|
print(f'\n=== {path} ===')
|
|
content = (BASE / path).read_text()
|
|
|
|
# cert-err33-c + cert-err34-c: replace sscanf with strtoul
|
|
if HEX_TO_BYTES_SSCANF_BIP39 in content:
|
|
content = content.replace(HEX_TO_BYTES_SSCANF_BIP39, HEX_TO_BYTES_STRTOUL_BIP39)
|
|
print(' ERR34: hex_to_bytes sscanf -> strtoul')
|
|
else:
|
|
print(' ERR34_SKIP: hex_to_bytes sscanf pattern not found')
|
|
|
|
# cert-err33-c: snprintf return unchecked
|
|
if BYTES_TO_HEX_OLD in content:
|
|
content = content.replace(BYTES_TO_HEX_OLD, BYTES_TO_HEX_NEW)
|
|
print(' ERR33: bytes_to_hex snprintf -> (void)snprintf')
|
|
else:
|
|
print(' ERR33_SKIP: bytes_to_hex pattern not found')
|
|
|
|
# clang-analyzer-core.NullDereference at L99
|
|
# CHECK(wl != nullptr, ...) then wl[0] - add explicit if
|
|
old_null = ' CHECK(wl != nullptr, "wordlist not null");\n CHECK(std::strcmp(wl[0]'
|
|
new_null = ' CHECK(wl != nullptr, "wordlist not null");\n if (!wl) { return; }\n CHECK(std::strcmp(wl[0]'
|
|
if old_null in content:
|
|
content = content.replace(old_null, new_null)
|
|
print(' NULL_DEREF: L99 added null guard')
|
|
else:
|
|
print(' NULL_DEREF_SKIP: pattern not found')
|
|
|
|
(BASE / path).write_text(content)
|
|
# Now add const to specific lines
|
|
lines = read(path)
|
|
|
|
# After the sscanf->strtoul replacement, L32 changes. The line numbers may shift.
|
|
# The original file had 393 lines. After replacing 14-line block with 7-line block
|
|
# and 9-line block with 9-line block (same), the const lines may shift.
|
|
# Let's handle const by string pattern instead.
|
|
# L238, L252, L264: std::string hex = bytes_to_hex(...) -> const std::string hex = ...
|
|
for idx in range(len(lines)):
|
|
line = lines[idx]
|
|
stripped = line.lstrip()
|
|
if stripped.startswith('std::string hex = bytes_to_hex('):
|
|
leading = line[:len(line) - len(stripped)]
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} std::string hex')
|
|
|
|
# L340, L352, L365: for (char c : mnemonic) -> for (const char c : mnemonic)
|
|
for idx in range(len(lines)):
|
|
line = lines[idx]
|
|
if 'for (char c : mnemonic)' in line:
|
|
lines[idx] = line.replace('for (char c : mnemonic)',
|
|
'for (const char c : mnemonic)')
|
|
print(f' CONST: L{idx+1} for (char c : mnemonic)')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/tests/test_ethereum.cpp
|
|
# ============================================================================
|
|
|
|
HEX_TO_BYTES_SSCANF_ETH = '''\
|
|
static void hex_to_bytes(const char* hex, uint8_t* out, size_t len) {
|
|
for (size_t i = 0; i < len; ++i) {
|
|
unsigned int byte = 0;
|
|
if (std::sscanf(hex + i * 2, "%02x", &byte) != 1) byte = 0;
|
|
out[i] = static_cast<uint8_t>(byte);
|
|
}
|
|
}'''
|
|
|
|
HEX_TO_BYTES_STRTOUL_ETH = '''\
|
|
static void hex_to_bytes(const char* hex, uint8_t* out, size_t len) {
|
|
for (size_t i = 0; i < len; ++i) {
|
|
char pair[3] = { hex[i * 2], hex[i * 2 + 1], '\\0' };
|
|
char* endptr = nullptr;
|
|
const unsigned long val = std::strtoul(pair, &endptr, 16);
|
|
out[i] = (endptr == pair + 2) ? static_cast<uint8_t>(val) : 0;
|
|
}
|
|
}'''
|
|
|
|
SNPRINTF_ETH_OLD = ' std::snprintf(buf, sizeof(buf), "Round-trip chain_id=%lu (%s)",'
|
|
SNPRINTF_ETH_NEW = ' (void)std::snprintf(buf, sizeof(buf), "Round-trip chain_id=%lu (%s)",'
|
|
|
|
|
|
def fix_test_ethereum():
|
|
path = 'cpu/tests/test_ethereum.cpp'
|
|
print(f'\n=== {path} ===')
|
|
content = (BASE / path).read_text()
|
|
|
|
# cert-err34-c: sscanf -> strtoul
|
|
if HEX_TO_BYTES_SSCANF_ETH in content:
|
|
content = content.replace(HEX_TO_BYTES_SSCANF_ETH, HEX_TO_BYTES_STRTOUL_ETH)
|
|
print(' ERR34: hex_to_bytes sscanf -> strtoul')
|
|
else:
|
|
print(' ERR34_SKIP: hex_to_bytes pattern not found')
|
|
|
|
# cert-err33-c at L352: snprintf return unchecked
|
|
if SNPRINTF_ETH_OLD in content:
|
|
content = content.replace(SNPRINTF_ETH_OLD, SNPRINTF_ETH_NEW)
|
|
print(' ERR33: snprintf -> (void)snprintf')
|
|
else:
|
|
print(' ERR33_SKIP: snprintf pattern not found')
|
|
|
|
# readability-simplify-boolean-expr: extract conditions to named bools
|
|
# L189: ASSERT_TRUE(sig.v == 27 || sig.v == 28, "legacy v should be 27 or 28");
|
|
# Fix: const bool v_ok = (sig.v == 27 || sig.v == 28); ASSERT_TRUE(v_ok, ...);
|
|
content = content.replace(
|
|
' ASSERT_TRUE(sig.v == 27 || sig.v == 28, "legacy v should be 27 or 28");',
|
|
' {\n const bool v_ok = (sig.v == 27 || sig.v == 28);\n ASSERT_TRUE(v_ok, "legacy v should be 27 or 28");\n }'
|
|
)
|
|
content = content.replace(
|
|
' ASSERT_TRUE(sig2.v == 37 || sig2.v == 38, "EIP-155 v should be 37 or 38");',
|
|
' {\n const bool v2_ok = (sig2.v == 37 || sig2.v == 38);\n ASSERT_TRUE(v2_ok, "EIP-155 v should be 37 or 38");\n }'
|
|
)
|
|
content = content.replace(
|
|
' ASSERT_TRUE(sig.v == 27 || sig.v == 28, "v should be 27 or 28");',
|
|
' {\n const bool v_ok2 = (sig.v == 27 || sig.v == 28);\n ASSERT_TRUE(v_ok2, "v should be 27 or 28");\n }'
|
|
)
|
|
print(' SIMPLIFY-BOOL: test_ethereum sig.v checks')
|
|
|
|
(BASE / path).write_text(content)
|
|
|
|
# Add const to variable declarations (by pattern)
|
|
lines = read(path)
|
|
|
|
# Find and fix const alerts: Point pk = ..., Scalar sk = ..., auto vars, etc.
|
|
# L226: Point pk = ... -> const Point pk
|
|
# L264: std::array<...> zero{} - this is const alert? Let me check
|
|
# Actually the const alerts at L226, L264, L287, L302, L309, L317, L333
|
|
# are all variable declarations that should be const
|
|
const_patterns = [
|
|
'Point pk = ',
|
|
'Point pk2 = ',
|
|
'auto expected_addr = ',
|
|
'auto addr = ',
|
|
'auto addr2 = ',
|
|
'std::array<uint8_t, 32> hash{};',
|
|
'std::array<uint8_t, 32> wrong_hash{};',
|
|
'bool wrong = ',
|
|
'bool wrong2 = ',
|
|
]
|
|
# Instead, use line numbers after adjusting for line-number shifts from replacements
|
|
# The simplify-bool fix added 3 blocks (each +4 lines = 3 lines inserted per block = +9 total)
|
|
# But let's use pattern matching instead of line numbers
|
|
|
|
# Pattern: find lines with variable declarations that are const-alerting
|
|
# Based on the alert line context I read:
|
|
# L226: Point pk = Point::generator().scalar_mul(sk);
|
|
# L264: std::array<uint8_t, 32> zero{};
|
|
# L287: Point pk = ...
|
|
# L302: bool valid = ...
|
|
# L309: bool wrong = ...
|
|
# L317: bool wrong2 = ...
|
|
# L333: Point pk = ...
|
|
|
|
for idx in range(len(lines)):
|
|
line = lines[idx]
|
|
stripped = line.lstrip()
|
|
leading = line[:len(line) - len(stripped)]
|
|
|
|
if stripped.startswith('const '):
|
|
continue
|
|
|
|
# Point pk = ... (not already const)
|
|
if re.match(r'Point pk\d? = ', stripped) and not stripped.startswith('const '):
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} Point pk')
|
|
elif re.match(r'(bool (valid|wrong\d?|r_zero|s_zero|all_zero)) = ', stripped):
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} bool')
|
|
elif re.match(r'std::array<uint8_t, 32> (hash|wrong_hash|zero)\{\}', stripped):
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} array')
|
|
elif re.match(r'auto expected_addr = ethernet_address_bytes', stripped) or \
|
|
re.match(r'auto expected_addr = ethereum_address_bytes', stripped):
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} auto expected_addr')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/tests/test_wallet.cpp
|
|
# ============================================================================
|
|
|
|
HEX_TO_BYTES_SSCANF_WALLET = '''\
|
|
static void hex_to_bytes(const char* hex, uint8_t* out, size_t len) {
|
|
for (size_t i = 0; i < len; ++i) {
|
|
unsigned int byte = 0;
|
|
if (std::sscanf(hex + i * 2, "%02x", &byte) != 1) byte = 0;
|
|
out[i] = static_cast<uint8_t>(byte);
|
|
}
|
|
}'''
|
|
|
|
HEX_TO_BYTES_STRTOUL_WALLET = '''\
|
|
static void hex_to_bytes(const char* hex, uint8_t* out, size_t len) {
|
|
for (size_t i = 0; i < len; ++i) {
|
|
char pair[3] = { hex[i * 2], hex[i * 2 + 1], '\\0' };
|
|
char* endptr = nullptr;
|
|
const unsigned long val = std::strtoul(pair, &endptr, 16);
|
|
out[i] = (endptr == pair + 2) ? static_cast<uint8_t>(val) : 0;
|
|
}
|
|
}'''
|
|
|
|
|
|
def fix_test_wallet():
|
|
path = 'cpu/tests/test_wallet.cpp'
|
|
print(f'\n=== {path} ===')
|
|
content = (BASE / path).read_text()
|
|
|
|
# misc-unused-using-decls: L45 'using fast::Point;'
|
|
if 'using fast::Point;\n' in content:
|
|
content = content.replace('using fast::Point;\n', '')
|
|
print(' UNUSED-USING: removed using fast::Point')
|
|
else:
|
|
print(' UNUSED-USING SKIP: using fast::Point not found')
|
|
|
|
# cert-err34-c: sscanf -> strtoul
|
|
if HEX_TO_BYTES_SSCANF_WALLET in content:
|
|
content = content.replace(HEX_TO_BYTES_SSCANF_WALLET, HEX_TO_BYTES_STRTOUL_WALLET)
|
|
print(' ERR34: hex_to_bytes sscanf -> strtoul')
|
|
else:
|
|
print(' ERR34_SKIP: hex_to_bytes sscanf pattern not found')
|
|
|
|
# readability-simplify-boolean-expr: extract to named bools
|
|
# L197: ASSERT_TRUE(wif[0] == 'K' || wif[0] == 'L', "WIF starts with K or L");
|
|
content = content.replace(
|
|
' ASSERT_TRUE(wif[0] == \'K\' || wif[0] == \'L\', "WIF starts with K or L");',
|
|
' {\n const bool wif_prefix_ok = (wif[0] == \'K\' || wif[0] == \'L\');\n ASSERT_TRUE(wif_prefix_ok, "WIF starts with K or L");\n }'
|
|
)
|
|
# L397: ASSERT_TRUE(sig.recid >= 0 && sig.recid <= 3, "valid recid");
|
|
content = content.replace(
|
|
' ASSERT_TRUE(sig.recid >= 0 && sig.recid <= 3, "valid recid");',
|
|
' {\n const bool recid_ok = (sig.recid >= 0 && sig.recid <= 3);\n ASSERT_TRUE(recid_ok, "valid recid");\n }'
|
|
)
|
|
# L505: ASSERT_TRUE(!btc.empty() && !ltc.empty() && !doge.empty(), "all non-empty");
|
|
content = content.replace(
|
|
' ASSERT_TRUE(!btc.empty() && !ltc.empty() && !doge.empty(), "all non-empty");',
|
|
' {\n const bool coins_non_empty = !btc.empty() && !ltc.empty() && !doge.empty();\n ASSERT_TRUE(coins_non_empty, "all non-empty");\n }'
|
|
)
|
|
# L602: multi-line ASSERT_TRUE
|
|
content = content.replace(
|
|
' ASSERT_TRUE(!p2pkh.empty() && !p2wpkh.empty() && !p2sh.empty() && !p2tr.empty(),\n "all non-empty");',
|
|
' {\n const bool addrs_non_empty = !p2pkh.empty() && !p2wpkh.empty() && !p2sh.empty() && !p2tr.empty();\n ASSERT_TRUE(addrs_non_empty, "all non-empty");\n }'
|
|
)
|
|
print(' SIMPLIFY-BOOL: 4 bool expressions extracted')
|
|
|
|
(BASE / path).write_text(content)
|
|
|
|
# Add const to variable declarations (by pattern matching)
|
|
lines = read(path)
|
|
for idx in range(len(lines)):
|
|
line = lines[idx]
|
|
stripped = line.lstrip()
|
|
leading = line[:len(line) - len(stripped)]
|
|
|
|
if stripped.startswith('const '):
|
|
continue
|
|
|
|
# L290: size_t msg_len = sizeof(msg) - 1;
|
|
# L293: bool ok = bitcoin_verify_message(...)
|
|
# L298: bool bad = bitcoin_verify_message(...)
|
|
# L314: size_t msg_len = sizeof(msg) - 1;
|
|
# L336: size_t msg_len = sizeof(msg) - 1;
|
|
# L366: size_t msg_len = sizeof(msg) - 1;
|
|
# L369: bool verified = verify_message(...)
|
|
# L418: size_t msg_len = sizeof(msg) - 1;
|
|
# L437: size_t msg_len = sizeof(msg) - 1;
|
|
if re.match(r'size_t msg_len = sizeof\(msg\) - 1;', stripped):
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} size_t msg_len')
|
|
elif re.match(r'bool ok = bitcoin_verify_message\(', stripped):
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} bool ok')
|
|
elif re.match(r'bool bad = bitcoin_verify_message\(', stripped):
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} bool bad')
|
|
elif re.match(r'bool verified = verify_message\(', stripped):
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} bool verified')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: cpu/tests/test_zk.cpp
|
|
# ============================================================================
|
|
|
|
def fix_test_zk():
|
|
path = 'cpu/tests/test_zk.cpp'
|
|
print(f'\n=== {path} ===')
|
|
lines = read(path)
|
|
|
|
# All 10 alerts are misc-const-correctness at:
|
|
# L60, L95, L103, L117, L134, L267, L281, L295, L309, L325
|
|
const_lines = [60, 95, 103, 117, 134, 267, 281, 295, 309, 325]
|
|
lines = add_const_to_lines(lines, const_lines, 'test_zk/')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# File: audit/test_ffi_round_trip.cpp
|
|
# ============================================================================
|
|
|
|
def fix_test_ffi():
|
|
path = 'audit/test_ffi_round_trip.cpp'
|
|
print(f'\n=== {path} ===')
|
|
content = (BASE / path).read_text()
|
|
|
|
# L1055: misc-redundant-expression (tautological check)
|
|
# Fix: remove the first redundant half of the OR expression
|
|
old_check = (
|
|
'CHECK(ufsecp_bip39_validate(ctx, "abandon abandon abandon abandon abandon abandon '
|
|
'abandon abandon abandon abandon abandon abandon") != UFSECP_OK\n'
|
|
' || ufsecp_bip39_validate(ctx, "abandon abandon abandon abandon '
|
|
'abandon abandon abandon abandon abandon abandon abandon abandon") == UFSECP_OK,\n'
|
|
' "bip39_validate accepts or rejects known mnemonic");'
|
|
)
|
|
new_check = (
|
|
'CHECK(ufsecp_bip39_validate(ctx, "abandon abandon abandon abandon abandon abandon '
|
|
'abandon abandon abandon abandon abandon abandon") == UFSECP_OK,\n'
|
|
' "bip39_validate accepts valid 12-word mnemonic");'
|
|
)
|
|
if old_check in content:
|
|
content = content.replace(old_check, new_check)
|
|
print(' REDUNDANT: L1055 tautological check fixed')
|
|
else:
|
|
print(' REDUNDANT_SKIP: L1055 exact pattern not found, trying partial match')
|
|
# Try a partial match
|
|
old_pattern = 'bip39_validate accepts or rejects known mnemonic'
|
|
if old_pattern in content:
|
|
# Need to find and replace the surrounding context
|
|
# Use regex for multi-line replacement
|
|
pattern = re.compile(
|
|
r'CHECK\(ufsecp_bip39_validate\(ctx,\s*"abandon[^"]+"\)\s*!=\s*UFSECP_OK\s*\n'
|
|
r'\s*\|\|\s*ufsecp_bip39_validate\(ctx,\s*"abandon[^"]+"\)\s*==\s*UFSECP_OK,\s*\n'
|
|
r'\s*"bip39_validate accepts or rejects known mnemonic"\)',
|
|
re.MULTILINE
|
|
)
|
|
replacement = (
|
|
'CHECK(ufsecp_bip39_validate(ctx, "abandon abandon abandon abandon abandon abandon '
|
|
'abandon abandon abandon abandon abandon abandon") == UFSECP_OK,\n'
|
|
' "bip39_validate accepts valid 12-word mnemonic")'
|
|
)
|
|
content, n = pattern.subn(replacement, content)
|
|
if n:
|
|
print(f' REDUNDANT: L1055 fixed via regex ({n} replacement)')
|
|
else:
|
|
print(' REDUNDANT_FAIL: could not fix L1055')
|
|
|
|
(BASE / path).write_text(content)
|
|
lines = read(path)
|
|
|
|
# L1317: size_t msg_len = 15; -> const size_t msg_len = 15;
|
|
# L1538: bool match = ... -> const bool match = ...
|
|
# Use pattern matching since line numbers may have shifted
|
|
for idx in range(len(lines)):
|
|
line = lines[idx]
|
|
stripped = line.lstrip()
|
|
leading = line[:len(line) - len(stripped)]
|
|
|
|
if stripped.startswith('const '):
|
|
continue
|
|
|
|
if stripped == 'size_t msg_len = 15;\n':
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} size_t msg_len = 15')
|
|
elif stripped.startswith('bool match = (std::memcmp('):
|
|
lines[idx] = leading + 'const ' + stripped
|
|
print(f' CONST: L{idx+1} bool match')
|
|
|
|
save(path, lines)
|
|
print(f' Saved {path} ({len(lines)} lines)')
|
|
|
|
|
|
# ============================================================================
|
|
# Main
|
|
# ============================================================================
|
|
|
|
if __name__ == '__main__':
|
|
print('Fix Round 4: resolving 211 code-scanning alerts')
|
|
print('=' * 60)
|
|
|
|
fix_ufsecp_impl()
|
|
fix_bip39()
|
|
fix_zk()
|
|
fix_message_signing()
|
|
fix_eth_signing()
|
|
fix_address()
|
|
fix_wallet()
|
|
fix_coin_address()
|
|
fix_test_bip39()
|
|
fix_test_ethereum()
|
|
fix_test_wallet()
|
|
fix_test_zk()
|
|
fix_test_ffi()
|
|
|
|
print('\n' + '=' * 60)
|
|
print('Done. Check brace balance:')
|
|
files = [
|
|
'include/ufsecp/ufsecp_impl.cpp',
|
|
'cpu/src/bip39.cpp',
|
|
'cpu/src/zk.cpp',
|
|
'cpu/src/message_signing.cpp',
|
|
'cpu/src/eth_signing.cpp',
|
|
'cpu/src/address.cpp',
|
|
'cpu/src/wallet.cpp',
|
|
'cpu/src/coin_address.cpp',
|
|
'cpu/tests/test_bip39.cpp',
|
|
'cpu/tests/test_ethereum.cpp',
|
|
'cpu/tests/test_wallet.cpp',
|
|
'cpu/tests/test_zk.cpp',
|
|
'audit/test_ffi_round_trip.cpp',
|
|
]
|
|
all_ok = True
|
|
for f in files:
|
|
try:
|
|
text = (BASE / f).read_text()
|
|
opens = text.count('{')
|
|
closes = text.count('}')
|
|
ok = opens == closes
|
|
status = 'OK' if ok else f'MISMATCH ({opens} vs {closes})'
|
|
print(f' {f}: {status}')
|
|
if not ok:
|
|
all_ok = False
|
|
except Exception as e:
|
|
print(f' {f}: ERROR {e}')
|
|
all_ok = False
|
|
|
|
if all_ok:
|
|
print('\nAll brace counts balanced.')
|
|
else:
|
|
print('\nWARNING: Some files have mismatched braces!')
|