Fix panics from fuzzing: checked byte helpers + TIFF parser hardening - #56

Open
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics
Open

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening#56
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics

Conversation

@lilith

Copy link
Copy Markdown

Summary

  • Convert byte-reading helpers to use checked_add + .get(), returning 0 on out-of-bounds instead of panicking — same function signatures, no caller changes
  • Harden TIFF parser: TiffEntry::new() returns Result with bounds validation, makernote parser uses .get() for magic-byte comparisons, FUJIFILM/IFD entry count validated
  • X3fDecoder::new() returns Result instead of .unwrap()
  • 4 unit tests + 9 integration tests covering valid-data equivalence, OOB behavior, usize overflow, and truncated/crafted inputs

See #54 for the discussion and approach comparison.

Why returning 0 is safe

The helpers are called in two contexts:

  1. Bit pumps / decode loops — stuffing zeros at end-of-stream is standard behavior, and chunks_exact() already guarantees bounds for the packed decoders, so the OOB case can't happen there anyway.
  2. Metadata parsing — a truncated field reading as 0 falls through to existing error handling (tag not found, invalid offset, etc). The targeted bounds checks in TiffEntry::new() and the IFD loop catch the cases where 0 would cause a downstream slice panic.

Unit tests verify the checked helpers return identical values to the originals for every valid position. The checked_add in the helpers also prevents a usize overflow panic when pos is near usize::MAX (caught by tests).

No performance impact

cargo asm confirms decode_16le produces identical assembly before and after. The entire release .rlib has zero panic_bounds_check calls — LLVM eliminates every bounds check, same as the original. Benchmarked with the built-in benchmark binary on DNG (LJPEG) and CR2 (LJPEG+Huffman) files — within run-to-run noise, no measurable difference.

Test plan

  • checked_helpers_match_unchecked_for_valid_data — no silent data corruption
  • checked_helpers_return_zero_on_oob — boundary, past-boundary, empty buffer
  • checked_helpers_no_wraparound_on_huge_pos — usize::MAX doesn't wrap
  • endian_methods_match_free_functions — Endian wrappers delegate correctly
  • 3 original fuzzing panic reproducers (FUJIFILM, X3F, TIFF IFD count)
  • Truncated TIFF, crafted IFD data offset, FUJIFILM at every length 8–107
  • Empty, 1-byte, and random-noise inputs

Convert byte-reading helpers to use checked_add + .get(), returning
0 on out-of-bounds. Same signatures, no caller changes, identical
asm output in release builds (LLVM eliminates the checks).
Harden TIFF parser against crafted inputs:
- TiffEntry::new() returns Result with checked arithmetic and
bounds validation for data ranges
- new_root() validates offset before slicing
- new_makernote() uses .get() for all magic-byte comparisons
- new_file() checks minimum buffer size for FUJIFILM branch
- TiffIFD::new() validates buffer has enough bytes for IFD entries
- get_u16()/get_u32() handle all 13 TIFF types correctly
- get_str() returns "" on invalid UTF-8 instead of panic_any
- copy_offset_from_parent() clamps to buffer bounds
- #[deny(clippy::indexing_slicing)] prevents regressions
Harden X3F parser:
- is_x3f() uses .get() for magic check
- X3fFile::new() validates size and directory offset
- X3fDecoder::new() returns Result instead of unwrapping
Includes regression tests for the 3 original fuzzing panics
(FUJIFILM short header, X3F crafted offset, TIFF large IFD count)
plus truncated TIFF and crafted entry data offset tests, and unit
tests proving the checked helpers match the originals on valid data.
Fixespedrocr#54
@lilith
lilithforce-pushed the fix-fuzzing-panics branch from c4e83ba to 42dd918CompareApril 8, 2026 02:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@lilith
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening - #56

Open
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics
Open

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening#56
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics

Conversation

@lilith

Copy link
Copy Markdown

Summary

  • Convert byte-reading helpers to use checked_add + .get(), returning 0 on out-of-bounds instead of panicking — same function signatures, no caller changes
  • Harden TIFF parser: TiffEntry::new() returns Result with bounds validation, makernote parser uses .get() for magic-byte comparisons, FUJIFILM/IFD entry count validated
  • X3fDecoder::new() returns Result instead of .unwrap()
  • 4 unit tests + 9 integration tests covering valid-data equivalence, OOB behavior, usize overflow, and truncated/crafted inputs

See #54 for the discussion and approach comparison.

Why returning 0 is safe

The helpers are called in two contexts:

  1. Bit pumps / decode loops — stuffing zeros at end-of-stream is standard behavior, and chunks_exact() already guarantees bounds for the packed decoders, so the OOB case can't happen there anyway.
  2. Metadata parsing — a truncated field reading as 0 falls through to existing error handling (tag not found, invalid offset, etc). The targeted bounds checks in TiffEntry::new() and the IFD loop catch the cases where 0 would cause a downstream slice panic.

Unit tests verify the checked helpers return identical values to the originals for every valid position. The checked_add in the helpers also prevents a usize overflow panic when pos is near usize::MAX (caught by tests).

No performance impact

cargo asm confirms decode_16le produces identical assembly before and after. The entire release .rlib has zero panic_bounds_check calls — LLVM eliminates every bounds check, same as the original. Benchmarked with the built-in benchmark binary on DNG (LJPEG) and CR2 (LJPEG+Huffman) files — within run-to-run noise, no measurable difference.

Test plan

  • checked_helpers_match_unchecked_for_valid_data — no silent data corruption
  • checked_helpers_return_zero_on_oob — boundary, past-boundary, empty buffer
  • checked_helpers_no_wraparound_on_huge_pos — usize::MAX doesn't wrap
  • endian_methods_match_free_functions — Endian wrappers delegate correctly
  • 3 original fuzzing panic reproducers (FUJIFILM, X3F, TIFF IFD count)
  • Truncated TIFF, crafted IFD data offset, FUJIFILM at every length 8–107
  • Empty, 1-byte, and random-noise inputs

Convert byte-reading helpers to use checked_add + .get(), returning
0 on out-of-bounds. Same signatures, no caller changes, identical
asm output in release builds (LLVM eliminates the checks).
Harden TIFF parser against crafted inputs:
- TiffEntry::new() returns Result with checked arithmetic and
bounds validation for data ranges
- new_root() validates offset before slicing
- new_makernote() uses .get() for all magic-byte comparisons
- new_file() checks minimum buffer size for FUJIFILM branch
- TiffIFD::new() validates buffer has enough bytes for IFD entries
- get_u16()/get_u32() handle all 13 TIFF types correctly
- get_str() returns "" on invalid UTF-8 instead of panic_any
- copy_offset_from_parent() clamps to buffer bounds
- #[deny(clippy::indexing_slicing)] prevents regressions
Harden X3F parser:
- is_x3f() uses .get() for magic check
- X3fFile::new() validates size and directory offset
- X3fDecoder::new() returns Result instead of unwrapping
Includes regression tests for the 3 original fuzzing panics
(FUJIFILM short header, X3F crafted offset, TIFF large IFD count)
plus truncated TIFF and crafted entry data offset tests, and unit
tests proving the checked helpers match the originals on valid data.
Fixespedrocr#54
@lilith
lilithforce-pushed the fix-fuzzing-panics branch from c4e83ba to 42dd918CompareApril 8, 2026 02:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@lilith
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening - #56

Open
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics
Open

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening#56
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics

Conversation

@lilith

Copy link
Copy Markdown

Summary

  • Convert byte-reading helpers to use checked_add + .get(), returning 0 on out-of-bounds instead of panicking — same function signatures, no caller changes
  • Harden TIFF parser: TiffEntry::new() returns Result with bounds validation, makernote parser uses .get() for magic-byte comparisons, FUJIFILM/IFD entry count validated
  • X3fDecoder::new() returns Result instead of .unwrap()
  • 4 unit tests + 9 integration tests covering valid-data equivalence, OOB behavior, usize overflow, and truncated/crafted inputs

See #54 for the discussion and approach comparison.

Why returning 0 is safe

The helpers are called in two contexts:

  1. Bit pumps / decode loops — stuffing zeros at end-of-stream is standard behavior, and chunks_exact() already guarantees bounds for the packed decoders, so the OOB case can't happen there anyway.
  2. Metadata parsing — a truncated field reading as 0 falls through to existing error handling (tag not found, invalid offset, etc). The targeted bounds checks in TiffEntry::new() and the IFD loop catch the cases where 0 would cause a downstream slice panic.

Unit tests verify the checked helpers return identical values to the originals for every valid position. The checked_add in the helpers also prevents a usize overflow panic when pos is near usize::MAX (caught by tests).

No performance impact

cargo asm confirms decode_16le produces identical assembly before and after. The entire release .rlib has zero panic_bounds_check calls — LLVM eliminates every bounds check, same as the original. Benchmarked with the built-in benchmark binary on DNG (LJPEG) and CR2 (LJPEG+Huffman) files — within run-to-run noise, no measurable difference.

Test plan

  • checked_helpers_match_unchecked_for_valid_data — no silent data corruption
  • checked_helpers_return_zero_on_oob — boundary, past-boundary, empty buffer
  • checked_helpers_no_wraparound_on_huge_pos — usize::MAX doesn't wrap
  • endian_methods_match_free_functions — Endian wrappers delegate correctly
  • 3 original fuzzing panic reproducers (FUJIFILM, X3F, TIFF IFD count)
  • Truncated TIFF, crafted IFD data offset, FUJIFILM at every length 8–107
  • Empty, 1-byte, and random-noise inputs

Convert byte-reading helpers to use checked_add + .get(), returning
0 on out-of-bounds. Same signatures, no caller changes, identical
asm output in release builds (LLVM eliminates the checks).
Harden TIFF parser against crafted inputs:
- TiffEntry::new() returns Result with checked arithmetic and
bounds validation for data ranges
- new_root() validates offset before slicing
- new_makernote() uses .get() for all magic-byte comparisons
- new_file() checks minimum buffer size for FUJIFILM branch
- TiffIFD::new() validates buffer has enough bytes for IFD entries
- get_u16()/get_u32() handle all 13 TIFF types correctly
- get_str() returns "" on invalid UTF-8 instead of panic_any
- copy_offset_from_parent() clamps to buffer bounds
- #[deny(clippy::indexing_slicing)] prevents regressions
Harden X3F parser:
- is_x3f() uses .get() for magic check
- X3fFile::new() validates size and directory offset
- X3fDecoder::new() returns Result instead of unwrapping
Includes regression tests for the 3 original fuzzing panics
(FUJIFILM short header, X3F crafted offset, TIFF large IFD count)
plus truncated TIFF and crafted entry data offset tests, and unit
tests proving the checked helpers match the originals on valid data.
Fixespedrocr#54
@lilith
lilithforce-pushed the fix-fuzzing-panics branch from c4e83ba to 42dd918CompareApril 8, 2026 02:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@lilith
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening - #56

Open
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics
Open

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening#56
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics

Conversation

@lilith

Copy link
Copy Markdown

Summary

  • Convert byte-reading helpers to use checked_add + .get(), returning 0 on out-of-bounds instead of panicking — same function signatures, no caller changes
  • Harden TIFF parser: TiffEntry::new() returns Result with bounds validation, makernote parser uses .get() for magic-byte comparisons, FUJIFILM/IFD entry count validated
  • X3fDecoder::new() returns Result instead of .unwrap()
  • 4 unit tests + 9 integration tests covering valid-data equivalence, OOB behavior, usize overflow, and truncated/crafted inputs

See #54 for the discussion and approach comparison.

Why returning 0 is safe

The helpers are called in two contexts:

  1. Bit pumps / decode loops — stuffing zeros at end-of-stream is standard behavior, and chunks_exact() already guarantees bounds for the packed decoders, so the OOB case can't happen there anyway.
  2. Metadata parsing — a truncated field reading as 0 falls through to existing error handling (tag not found, invalid offset, etc). The targeted bounds checks in TiffEntry::new() and the IFD loop catch the cases where 0 would cause a downstream slice panic.

Unit tests verify the checked helpers return identical values to the originals for every valid position. The checked_add in the helpers also prevents a usize overflow panic when pos is near usize::MAX (caught by tests).

No performance impact

cargo asm confirms decode_16le produces identical assembly before and after. The entire release .rlib has zero panic_bounds_check calls — LLVM eliminates every bounds check, same as the original. Benchmarked with the built-in benchmark binary on DNG (LJPEG) and CR2 (LJPEG+Huffman) files — within run-to-run noise, no measurable difference.

Test plan

  • checked_helpers_match_unchecked_for_valid_data — no silent data corruption
  • checked_helpers_return_zero_on_oob — boundary, past-boundary, empty buffer
  • checked_helpers_no_wraparound_on_huge_pos — usize::MAX doesn't wrap
  • endian_methods_match_free_functions — Endian wrappers delegate correctly
  • 3 original fuzzing panic reproducers (FUJIFILM, X3F, TIFF IFD count)
  • Truncated TIFF, crafted IFD data offset, FUJIFILM at every length 8–107
  • Empty, 1-byte, and random-noise inputs

Convert byte-reading helpers to use checked_add + .get(), returning
0 on out-of-bounds. Same signatures, no caller changes, identical
asm output in release builds (LLVM eliminates the checks).
Harden TIFF parser against crafted inputs:
- TiffEntry::new() returns Result with checked arithmetic and
bounds validation for data ranges
- new_root() validates offset before slicing
- new_makernote() uses .get() for all magic-byte comparisons
- new_file() checks minimum buffer size for FUJIFILM branch
- TiffIFD::new() validates buffer has enough bytes for IFD entries
- get_u16()/get_u32() handle all 13 TIFF types correctly
- get_str() returns "" on invalid UTF-8 instead of panic_any
- copy_offset_from_parent() clamps to buffer bounds
- #[deny(clippy::indexing_slicing)] prevents regressions
Harden X3F parser:
- is_x3f() uses .get() for magic check
- X3fFile::new() validates size and directory offset
- X3fDecoder::new() returns Result instead of unwrapping
Includes regression tests for the 3 original fuzzing panics
(FUJIFILM short header, X3F crafted offset, TIFF large IFD count)
plus truncated TIFF and crafted entry data offset tests, and unit
tests proving the checked helpers match the originals on valid data.
Fixespedrocr#54
@lilith
lilithforce-pushed the fix-fuzzing-panics branch from c4e83ba to 42dd918CompareApril 8, 2026 02:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@lilith
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening - #56

Open
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics
Open

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening#56
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics

Conversation

@lilith

Copy link
Copy Markdown

Summary

  • Convert byte-reading helpers to use checked_add + .get(), returning 0 on out-of-bounds instead of panicking — same function signatures, no caller changes
  • Harden TIFF parser: TiffEntry::new() returns Result with bounds validation, makernote parser uses .get() for magic-byte comparisons, FUJIFILM/IFD entry count validated
  • X3fDecoder::new() returns Result instead of .unwrap()
  • 4 unit tests + 9 integration tests covering valid-data equivalence, OOB behavior, usize overflow, and truncated/crafted inputs

See #54 for the discussion and approach comparison.

Why returning 0 is safe

The helpers are called in two contexts:

  1. Bit pumps / decode loops — stuffing zeros at end-of-stream is standard behavior, and chunks_exact() already guarantees bounds for the packed decoders, so the OOB case can't happen there anyway.
  2. Metadata parsing — a truncated field reading as 0 falls through to existing error handling (tag not found, invalid offset, etc). The targeted bounds checks in TiffEntry::new() and the IFD loop catch the cases where 0 would cause a downstream slice panic.

Unit tests verify the checked helpers return identical values to the originals for every valid position. The checked_add in the helpers also prevents a usize overflow panic when pos is near usize::MAX (caught by tests).

No performance impact

cargo asm confirms decode_16le produces identical assembly before and after. The entire release .rlib has zero panic_bounds_check calls — LLVM eliminates every bounds check, same as the original. Benchmarked with the built-in benchmark binary on DNG (LJPEG) and CR2 (LJPEG+Huffman) files — within run-to-run noise, no measurable difference.

Test plan

  • checked_helpers_match_unchecked_for_valid_data — no silent data corruption
  • checked_helpers_return_zero_on_oob — boundary, past-boundary, empty buffer
  • checked_helpers_no_wraparound_on_huge_pos — usize::MAX doesn't wrap
  • endian_methods_match_free_functions — Endian wrappers delegate correctly
  • 3 original fuzzing panic reproducers (FUJIFILM, X3F, TIFF IFD count)
  • Truncated TIFF, crafted IFD data offset, FUJIFILM at every length 8–107
  • Empty, 1-byte, and random-noise inputs

Convert byte-reading helpers to use checked_add + .get(), returning
0 on out-of-bounds. Same signatures, no caller changes, identical
asm output in release builds (LLVM eliminates the checks).
Harden TIFF parser against crafted inputs:
- TiffEntry::new() returns Result with checked arithmetic and
bounds validation for data ranges
- new_root() validates offset before slicing
- new_makernote() uses .get() for all magic-byte comparisons
- new_file() checks minimum buffer size for FUJIFILM branch
- TiffIFD::new() validates buffer has enough bytes for IFD entries
- get_u16()/get_u32() handle all 13 TIFF types correctly
- get_str() returns "" on invalid UTF-8 instead of panic_any
- copy_offset_from_parent() clamps to buffer bounds
- #[deny(clippy::indexing_slicing)] prevents regressions
Harden X3F parser:
- is_x3f() uses .get() for magic check
- X3fFile::new() validates size and directory offset
- X3fDecoder::new() returns Result instead of unwrapping
Includes regression tests for the 3 original fuzzing panics
(FUJIFILM short header, X3F crafted offset, TIFF large IFD count)
plus truncated TIFF and crafted entry data offset tests, and unit
tests proving the checked helpers match the originals on valid data.
Fixespedrocr#54
@lilith
lilithforce-pushed the fix-fuzzing-panics branch from c4e83ba to 42dd918CompareApril 8, 2026 02:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@lilith
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening - #56

Open
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics
Open

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening#56
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics

Conversation

@lilith

Copy link
Copy Markdown

Summary

  • Convert byte-reading helpers to use checked_add + .get(), returning 0 on out-of-bounds instead of panicking — same function signatures, no caller changes
  • Harden TIFF parser: TiffEntry::new() returns Result with bounds validation, makernote parser uses .get() for magic-byte comparisons, FUJIFILM/IFD entry count validated
  • X3fDecoder::new() returns Result instead of .unwrap()
  • 4 unit tests + 9 integration tests covering valid-data equivalence, OOB behavior, usize overflow, and truncated/crafted inputs

See #54 for the discussion and approach comparison.

Why returning 0 is safe

The helpers are called in two contexts:

  1. Bit pumps / decode loops — stuffing zeros at end-of-stream is standard behavior, and chunks_exact() already guarantees bounds for the packed decoders, so the OOB case can't happen there anyway.
  2. Metadata parsing — a truncated field reading as 0 falls through to existing error handling (tag not found, invalid offset, etc). The targeted bounds checks in TiffEntry::new() and the IFD loop catch the cases where 0 would cause a downstream slice panic.

Unit tests verify the checked helpers return identical values to the originals for every valid position. The checked_add in the helpers also prevents a usize overflow panic when pos is near usize::MAX (caught by tests).

No performance impact

cargo asm confirms decode_16le produces identical assembly before and after. The entire release .rlib has zero panic_bounds_check calls — LLVM eliminates every bounds check, same as the original. Benchmarked with the built-in benchmark binary on DNG (LJPEG) and CR2 (LJPEG+Huffman) files — within run-to-run noise, no measurable difference.

Test plan

  • checked_helpers_match_unchecked_for_valid_data — no silent data corruption
  • checked_helpers_return_zero_on_oob — boundary, past-boundary, empty buffer
  • checked_helpers_no_wraparound_on_huge_pos — usize::MAX doesn't wrap
  • endian_methods_match_free_functions — Endian wrappers delegate correctly
  • 3 original fuzzing panic reproducers (FUJIFILM, X3F, TIFF IFD count)
  • Truncated TIFF, crafted IFD data offset, FUJIFILM at every length 8–107
  • Empty, 1-byte, and random-noise inputs

Convert byte-reading helpers to use checked_add + .get(), returning
0 on out-of-bounds. Same signatures, no caller changes, identical
asm output in release builds (LLVM eliminates the checks).
Harden TIFF parser against crafted inputs:
- TiffEntry::new() returns Result with checked arithmetic and
bounds validation for data ranges
- new_root() validates offset before slicing
- new_makernote() uses .get() for all magic-byte comparisons
- new_file() checks minimum buffer size for FUJIFILM branch
- TiffIFD::new() validates buffer has enough bytes for IFD entries
- get_u16()/get_u32() handle all 13 TIFF types correctly
- get_str() returns "" on invalid UTF-8 instead of panic_any
- copy_offset_from_parent() clamps to buffer bounds
- #[deny(clippy::indexing_slicing)] prevents regressions
Harden X3F parser:
- is_x3f() uses .get() for magic check
- X3fFile::new() validates size and directory offset
- X3fDecoder::new() returns Result instead of unwrapping
Includes regression tests for the 3 original fuzzing panics
(FUJIFILM short header, X3F crafted offset, TIFF large IFD count)
plus truncated TIFF and crafted entry data offset tests, and unit
tests proving the checked helpers match the originals on valid data.
Fixespedrocr#54
@lilith
lilithforce-pushed the fix-fuzzing-panics branch from c4e83ba to 42dd918CompareApril 8, 2026 02:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@lilith
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening - #56

Open
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics
Open

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening#56
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics

Conversation

@lilith

Copy link
Copy Markdown

Summary

  • Convert byte-reading helpers to use checked_add + .get(), returning 0 on out-of-bounds instead of panicking — same function signatures, no caller changes
  • Harden TIFF parser: TiffEntry::new() returns Result with bounds validation, makernote parser uses .get() for magic-byte comparisons, FUJIFILM/IFD entry count validated
  • X3fDecoder::new() returns Result instead of .unwrap()
  • 4 unit tests + 9 integration tests covering valid-data equivalence, OOB behavior, usize overflow, and truncated/crafted inputs

See #54 for the discussion and approach comparison.

Why returning 0 is safe

The helpers are called in two contexts:

  1. Bit pumps / decode loops — stuffing zeros at end-of-stream is standard behavior, and chunks_exact() already guarantees bounds for the packed decoders, so the OOB case can't happen there anyway.
  2. Metadata parsing — a truncated field reading as 0 falls through to existing error handling (tag not found, invalid offset, etc). The targeted bounds checks in TiffEntry::new() and the IFD loop catch the cases where 0 would cause a downstream slice panic.

Unit tests verify the checked helpers return identical values to the originals for every valid position. The checked_add in the helpers also prevents a usize overflow panic when pos is near usize::MAX (caught by tests).

No performance impact

cargo asm confirms decode_16le produces identical assembly before and after. The entire release .rlib has zero panic_bounds_check calls — LLVM eliminates every bounds check, same as the original. Benchmarked with the built-in benchmark binary on DNG (LJPEG) and CR2 (LJPEG+Huffman) files — within run-to-run noise, no measurable difference.

Test plan

  • checked_helpers_match_unchecked_for_valid_data — no silent data corruption
  • checked_helpers_return_zero_on_oob — boundary, past-boundary, empty buffer
  • checked_helpers_no_wraparound_on_huge_pos — usize::MAX doesn't wrap
  • endian_methods_match_free_functions — Endian wrappers delegate correctly
  • 3 original fuzzing panic reproducers (FUJIFILM, X3F, TIFF IFD count)
  • Truncated TIFF, crafted IFD data offset, FUJIFILM at every length 8–107
  • Empty, 1-byte, and random-noise inputs

Convert byte-reading helpers to use checked_add + .get(), returning
0 on out-of-bounds. Same signatures, no caller changes, identical
asm output in release builds (LLVM eliminates the checks).
Harden TIFF parser against crafted inputs:
- TiffEntry::new() returns Result with checked arithmetic and
bounds validation for data ranges
- new_root() validates offset before slicing
- new_makernote() uses .get() for all magic-byte comparisons
- new_file() checks minimum buffer size for FUJIFILM branch
- TiffIFD::new() validates buffer has enough bytes for IFD entries
- get_u16()/get_u32() handle all 13 TIFF types correctly
- get_str() returns "" on invalid UTF-8 instead of panic_any
- copy_offset_from_parent() clamps to buffer bounds
- #[deny(clippy::indexing_slicing)] prevents regressions
Harden X3F parser:
- is_x3f() uses .get() for magic check
- X3fFile::new() validates size and directory offset
- X3fDecoder::new() returns Result instead of unwrapping
Includes regression tests for the 3 original fuzzing panics
(FUJIFILM short header, X3F crafted offset, TIFF large IFD count)
plus truncated TIFF and crafted entry data offset tests, and unit
tests proving the checked helpers match the originals on valid data.
Fixespedrocr#54
@lilith
lilithforce-pushed the fix-fuzzing-panics branch from c4e83ba to 42dd918CompareApril 8, 2026 02:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@lilith
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening - #56

Open
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics
Open

Fix panics from fuzzing: checked byte helpers + TIFF parser hardening#56
lilith wants to merge 1 commit into
pedrocr:masterfrom
lilith:fix-fuzzing-panics

Conversation

@lilith

Copy link
Copy Markdown

Summary

  • Convert byte-reading helpers to use checked_add + .get(), returning 0 on out-of-bounds instead of panicking — same function signatures, no caller changes
  • Harden TIFF parser: TiffEntry::new() returns Result with bounds validation, makernote parser uses .get() for magic-byte comparisons, FUJIFILM/IFD entry count validated
  • X3fDecoder::new() returns Result instead of .unwrap()
  • 4 unit tests + 9 integration tests covering valid-data equivalence, OOB behavior, usize overflow, and truncated/crafted inputs

See #54 for the discussion and approach comparison.

Why returning 0 is safe

The helpers are called in two contexts:

  1. Bit pumps / decode loops — stuffing zeros at end-of-stream is standard behavior, and chunks_exact() already guarantees bounds for the packed decoders, so the OOB case can't happen there anyway.
  2. Metadata parsing — a truncated field reading as 0 falls through to existing error handling (tag not found, invalid offset, etc). The targeted bounds checks in TiffEntry::new() and the IFD loop catch the cases where 0 would cause a downstream slice panic.

Unit tests verify the checked helpers return identical values to the originals for every valid position. The checked_add in the helpers also prevents a usize overflow panic when pos is near usize::MAX (caught by tests).

No performance impact

cargo asm confirms decode_16le produces identical assembly before and after. The entire release .rlib has zero panic_bounds_check calls — LLVM eliminates every bounds check, same as the original. Benchmarked with the built-in benchmark binary on DNG (LJPEG) and CR2 (LJPEG+Huffman) files — within run-to-run noise, no measurable difference.

Test plan

  • checked_helpers_match_unchecked_for_valid_data — no silent data corruption
  • checked_helpers_return_zero_on_oob — boundary, past-boundary, empty buffer
  • checked_helpers_no_wraparound_on_huge_pos — usize::MAX doesn't wrap
  • endian_methods_match_free_functions — Endian wrappers delegate correctly
  • 3 original fuzzing panic reproducers (FUJIFILM, X3F, TIFF IFD count)
  • Truncated TIFF, crafted IFD data offset, FUJIFILM at every length 8–107
  • Empty, 1-byte, and random-noise inputs

Convert byte-reading helpers to use checked_add + .get(), returning
0 on out-of-bounds. Same signatures, no caller changes, identical
asm output in release builds (LLVM eliminates the checks).
Harden TIFF parser against crafted inputs:
- TiffEntry::new() returns Result with checked arithmetic and
bounds validation for data ranges
- new_root() validates offset before slicing
- new_makernote() uses .get() for all magic-byte comparisons
- new_file() checks minimum buffer size for FUJIFILM branch
- TiffIFD::new() validates buffer has enough bytes for IFD entries
- get_u16()/get_u32() handle all 13 TIFF types correctly
- get_str() returns "" on invalid UTF-8 instead of panic_any
- copy_offset_from_parent() clamps to buffer bounds
- #[deny(clippy::indexing_slicing)] prevents regressions
Harden X3F parser:
- is_x3f() uses .get() for magic check
- X3fFile::new() validates size and directory offset
- X3fDecoder::new() returns Result instead of unwrapping
Includes regression tests for the 3 original fuzzing panics
(FUJIFILM short header, X3F crafted offset, TIFF large IFD count)
plus truncated TIFF and crafted entry data offset tests, and unit
tests proving the checked helpers match the originals on valid data.
Fixespedrocr#54
@lilith
lilithforce-pushed the fix-fuzzing-panics branch from c4e83ba to 42dd918CompareApril 8, 2026 02:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@lilith