Skip to content

feat: prepare rork-hook 0.1.0 package#1

Merged
mozharovsky merged 3 commits into
mainfrom
codex/prepare-rork-hook-0.1.0
Jun 1, 2026
Merged

feat: prepare rork-hook 0.1.0 package#1
mozharovsky merged 3 commits into
mainfrom
codex/prepare-rork-hook-0.1.0

Conversation

@mozharovsky

@mozharovsky mozharovsky commented Jun 1, 2026

Copy link
Copy Markdown
Member

Summary

  • Add the standalone source-only SwiftPM RorkHook package with public C ABI headers and Swift/ObjC import support.
  • Add README usage examples and license guidance for Swift and Objective-C consumers.
  • Add correctness coverage for arm64 instruction decoding, protected pointer writes, loaded-image symbol lookup, and image-local rebinding.
  • Fix shifted ADD (immediate) decoding so byte-offset decoding handles both legal shift forms.
  • Add 0.1.0 version probes, provenance, architecture/safety/release docs, CI, and a downstream client smoke script.
  • Port the existing RorkHook implementation and host tests into the public repo structure.

Verification

  • swift test
  • Scripts/smoke-client-package.sh

Summary by CodeRabbit

Release Notes

  • New Features

    • Initial release of RorkHook, a Swift Package Manager–distributed runtime patching toolkit for Apple platforms.
    • Support for memory protection changes, function replacement, symbol lookup, and symbol rebinding capabilities.
    • Public C ABI for integration with C and Objective-C applications.
  • Documentation

    • Comprehensive architecture, safety, and release documentation.
    • README with package structure and usage instructions.
  • Chores

    • GitHub Actions CI workflow for automated testing.
    • Smoke-test infrastructure for downstream integration validation.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mozharovsky, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 27 minutes and 11 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4be3bb60-9353-4846-b613-85df585436e6

📥 Commits

Reviewing files that changed from the base of the PR and between 3f84469 and b09bca8.

📒 Files selected for processing (39)
  • .github/workflows/ci.yml
  • .gitignore
  • CHANGELOG.md
  • Docs/Architecture.md
  • Docs/Release.md
  • Docs/Safety.md
  • NOTICE.md
  • Package.swift
  • README.md
  • Scripts/smoke-client-package.sh
  • Sources/RorkHook/RorkHookArm64.c
  • Sources/RorkHook/RorkHookFunction.c
  • Sources/RorkHook/RorkHookInternal.c
  • Sources/RorkHook/RorkHookMemory.c
  • Sources/RorkHook/RorkHookRebind.c
  • Sources/RorkHook/RorkHookSharedCache.c
  • Sources/RorkHook/RorkHookSymbols.c
  • Sources/RorkHook/RorkHookVersion.c
  • Sources/RorkHook/include/RorkHook.h
  • Sources/RorkHook/include/RorkHook/RorkHook.h
  • Sources/RorkHook/include/RorkHookArm64.h
  • Sources/RorkHook/include/RorkHookFunction.h
  • Sources/RorkHook/include/RorkHookMemory.h
  • Sources/RorkHook/include/RorkHookRebind.h
  • Sources/RorkHook/include/RorkHookSharedCache.h
  • Sources/RorkHook/include/RorkHookSymbols.h
  • Sources/RorkHook/include/RorkHookTypes.h
  • Sources/RorkHook/include/RorkHookVersion.h
  • Sources/RorkHook/include/module.modulemap
  • Sources/RorkHook/private/RorkHookDyldCache.h
  • Sources/RorkHook/private/RorkHookInternal.h
  • Tests/RorkHookTestSupport/RorkHookTestSupport.m
  • Tests/RorkHookTestSupport/include/RorkHookTestSupport.h
  • Tests/RorkHookTests/RorkHookArm64Tests.swift
  • Tests/RorkHookTests/RorkHookEnvironmentTests.swift
  • Tests/RorkHookTests/RorkHookFunctionTests.swift
  • Tests/RorkHookTests/RorkHookMemoryTests.swift
  • Tests/RorkHookTests/RorkHookRebindTests.swift
  • Tests/RorkHookTests/RorkHookSymbolTests.swift
📝 Walkthrough

Walkthrough

Rork Hook adds a source-only SwiftPM package exposing a C ABI for runtime patching: headers, implementations for Mach-O symbol lookup, dyld shared-cache resolution, arm64 decoding, memory protection/TPRO, function detours and rebinding, plus tests, smoke scripts, manifest, and CI.

Changes

Rork Hook Core Implementation and Testing

Layer / File(s) Summary
Docs and project foundation
CHANGELOG.md, Docs/Architecture.md, Docs/Release.md, Docs/Safety.md, NOTICE.md, .gitignore, README.md
Adds changelog, architecture/release/safety docs, provenance notice, README expansion, and .gitignore entries.
SPM manifest and CI
Package.swift, .github/workflows/ci.yml
Defines RorkHook static product, explicit C targets/headers, language standards, test targets, and a GitHub Actions workflow that runs swift test and the smoke-client script.
Public C ABI headers & module
Sources/RorkHook/include/*, module.modulemap
Introduces umbrella and component headers (RorkHookTypes.h, RorkHookVersion.h, RorkHookMemory.h, RorkHookArm64.h, RorkHookSymbols.h, RorkHookSharedCache.h, RorkHookFunction.h, RorkHookRebind.h) and a Clang module map.
Internal infra and version
Sources/RorkHook/private/*, Sources/RorkHook/RorkHookVersion.c, Sources/RorkHook/RorkHookInternal.c
Adds private dyld-cache structs, load-command/image helpers, memory-readability checks, pointer-signing helpers, shared-cache slide query, and version/ABI accessors.
ARM64 instruction decoding
Sources/RorkHook/RorkHookArm64.c, tests Tests/RorkHookTests/*Arm64*
Adds sign-extension, ADRP/ADD/LDR/LDUR/MOVZ decoders and a branch follower with unit tests validating decoding and branch-follow behavior.
Memory protection and TPRO support
Sources/RorkHook/RorkHookMemory.c, Sources/RorkHook/include/RorkHookMemory.h, tests Tests/RorkHookTests/*Memory*
Implements raw trap path for arm64 iOS, vm_protect wrappers, page queries, protected-pointer writes with TPRO fallback, dcache flush, and TPRO thread window controls with tests.
Function absolute-jump generation & replace
Sources/RorkHook/RorkHookFunction.c, Sources/RorkHook/include/RorkHookFunction.h, tests Tests/RorkHookTests/*Function*
Encodes fixed 5-word ARM64 absolute jumps (MOVK lanes + BR), builds stubs, overwrites prologues with permission toggles and icache invalidation; includes unit tests for encoding and argument validation.
Symbol lookup and shared-cache resolution
Sources/RorkHook/RorkHookSymbols.c, Sources/RorkHook/RorkHookSharedCache.c
Implements LC_SYMTAB/__LINKEDIT scanning for live/image-file symbols and on-disk dyld shared-cache discovery plus local-symbols sidecar parsing for private symbols.
Per-image and global rebinding
Sources/RorkHook/RorkHookRebind.c, Sources/RorkHook/include/RorkHookRebind.h
Implements pointer-auth-aware slot comparison and rewriting, protected writes, per-image rebinding APIs, and a thread-safe global registry with dyld add-image callback and self-exclusion.
Test-support and unit tests
Tests/RorkHookTestSupport/*, Tests/RorkHookTests/*
Adds Objective-C test-support image and comprehensive XCTest suites covering environment, arm64 decoding, function encoding, memory protection, rebind, and symbol resolution.
Smoke-test script
Scripts/smoke-client-package.sh
Adds a script that synthesizes a downstream Swift package and builds it (swift build and optional xcodebuild) to smoke-test the public C ABI consumption.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

"🐰 I hopped through headers, tests, and traps,
Built MOVKs and chased elusive maps,
I signed pointers where the code is tight,
Rebound functions, then slept at night.
Bravo—small feet, big patching naps."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.72% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: prepare rork-hook 0.1.0 package' is a concise, single sentence that directly summarizes the primary change: preparing and releasing the initial 0.1.0 version of the RorkHook package as a standalone SwiftPM source-only library.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/prepare-rork-hook-0.1.0

Comment @coderabbitai help to get the list of available commands and usage tips.

@mozharovsky mozharovsky force-pushed the codex/prepare-rork-hook-0.1.0 branch 5 times, most recently from f86403e to a44785b Compare June 1, 2026 14:40

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
Sources/RorkHook/RorkHookArm64.c (2)

24-40: 💤 Low value

Dead code: shift handling is unreachable due to mask.

The mask 0xffc00000 includes bit 22 (the sh field), and the expected value 0x91000000 requires sh=0. This means only unshifted ADD instructions pass the check, making lines 37-38 unreachable—shift will always be 0.

If the intent is to support both shifted (LSL #12) and unshifted forms, relax the mask to exclude bit 22. If only unshifted is intended, remove the dead code.

Option A: Support both shifted and unshifted forms
 bool RorkHookDecodeADDImmediate(uint32_t instruction,
                                 uint8_t baseRegister,
                                 uintptr_t *offset) {
-    if ((instruction & 0xffc00000u) != 0x91000000u) {
+    if ((instruction & 0xff800000u) != 0x91000000u) {
         return false;
     }
Option B: Keep unshifted-only and remove dead code
     uint32_t imm12 = (instruction >> 10) & 0xfffu;
-    uint32_t shift = (instruction >> 22) & 0x3u;
-    *offset = shift == 1 ? ((uintptr_t)imm12 << 12) : imm12;
+    *offset = imm12;
     return true;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/RorkHook/RorkHookArm64.c` around lines 24 - 40, The mask in
RorkHookDecodeADDImmediate currently includes the sh bit (bit 22), making the
extracted shift always zero; to support both unshifted and LSL#12 forms, relax
the compare mask from 0xffc00000u to 0xff800000u (i.e., clear bit 22 in the
mask) so the check ((instruction & 0xff800000u) != 0x91000000u) allows either
sh=0 or sh=1, keep the existing shift extraction ((instruction >> 22) & 0x3u)
and the offset computation, or if you intend only the unshifted form, remove the
unreachable shift/LSL#12 branch and simplify to *offset = imm12 after keeping
the original mask.

103-111: 💤 Low value

Mask matches both B and BL—possibly unintentional.

The mask 0x7c000000 does not check bit 31, so both B (unconditional branch) and BL (branch with link) pass the check. For detour/thunk following, typically only B is desired since BL is a function call that expects to return.

If only B is intended, include bit 31 in the mask.

Suggested fix to match only B
 uint32_t *RorkHookFollowOneBranch(uint32_t *instructions) {
     uint32_t instruction = instructions[0];
-    if ((instruction & 0x7c000000u) != 0x14000000u) {
+    if ((instruction & 0xfc000000u) != 0x14000000u) {
         return instructions;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/RorkHook/RorkHookArm64.c` around lines 103 - 111, The check in
RorkHookFollowOneBranch currently uses mask 0x7c000000 which doesn't include bit
31 and therefore matches both B and BL; to match only unconditional B, change
the mask to include bit 31 (e.g. replace 0x7c000000u with 0xFC000000u) so the
comparison (instruction & 0xFC000000u) == 0x14000000u only succeeds for B; keep
the rest of the function (including the use of RorkHookArm64SignExtend)
unchanged.
Sources/RorkHook/RorkHookInternal.c (1)

127-153: 💤 Low value

Optional: dedup the vm_region_64 query shared with RorkHookMemoryIsReadable.

This block repeats the same region lookup as RorkHookMemoryIsReadable (Lines 101-118), differing only in which protection bit it tests. Extracting a small helper that returns the region's protection (or a "found" flag plus prot bits) would remove the duplication and keep the two call sites in sync.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/RorkHook/RorkHookInternal.c` around lines 127 - 153, Duplicate
vm_region_64 lookup logic in RorkHookSignPointerIfExecutable and
RorkHookMemoryIsReadable should be extracted into a small helper (e.g.,
RorkHookQueryRegionProtection) that takes a pointer, performs vm_region_64 and
mach_port_deallocate, and returns whether the region was found plus its
protection bits; then update RorkHookSignPointerIfExecutable to call that helper
and test VM_PROT_EXECUTE, and update RorkHookMemoryIsReadable to call the same
helper and test the readable bit, so both call sites share the same region-query
implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 15-16: The checkout step currently uses the tag
actions/checkout@v4 and lacks explicit credential hardening; update the uses
value to the full 40-char commit SHA for actions/checkout (replace
actions/checkout@v4 with actions/checkout@<full-commit-sha>) and add
persist-credentials: false under the same Checkout step to disable token
persistence; ensure the step named "Checkout" contains both the SHA-pinned uses
and the persist-credentials: false key.

In `@Scripts/smoke-client-package.sh`:
- Around line 26-42: The generated Package.swift currently hardcodes the SwiftPM
package identity as "rork-hook" while using .package(path: "$ROOT_DIR"), which
will break if the checkout directory basename differs; derive a PACKAGE_ID from
ROOT_DIR (e.g., canonicalize basename and strip any .git suffix) and interpolate
that variable for both .product(..., package: PACKAGE_ID) occurrences used by
the "ObjCSmoke" and "ClientSmoke" targets so the .package(path: "$ROOT_DIR") and
.product references use the same computed package identity.

In `@Sources/RorkHook/include/RorkHook.h`:
- Around line 10-23: The file header says the library surface is grouped into
"four concerns" but the bullet list actually contains five items
(RorkHookMemory.h, RorkHookSymbols.h, RorkHookSharedCache.h, RorkHookArm64.h,
and the Detours group: RorkHookFunction.h / RorkHookRebind.h); update the prose
to match the list by changing "four concerns" to "five concerns" (or
remove/merge a bullet if you intended four), ensuring the symbols
RorkHookMemory.h, RorkHookSymbols.h, RorkHookSharedCache.h, RorkHookArm64.h,
RorkHookFunction.h and RorkHookRebind.h are referenced consistently.

In `@Sources/RorkHook/include/RorkHookSymbols.h`:
- Around line 10-28: Annotate the public pointer/nullable APIs by including
RorkHookTypes.h at the top of RorkHookSymbols.h and wrap the declarations of
RorkHookFindSymbol and RorkHookFindSymbolInFileImage with
RORK_HOOK_ASSUME_NONNULL (and mark the nullable return with RORK_HOOK_NULLABLE),
and mark pointer parameters (const RorkHookMachHeader *header, const char
*symbolName) as nonnull per the docs; apply the same pattern to sibling headers
that expose pointer params or void* results (e.g., RorkHookSharedCache.h,
RorkHookMemory.h, RorkHookArm64.h) so every public pointer parameter uses
RORK_HOOK_ASSUME_NONNULL for the region and RORK_HOOK_NULLABLE for nullable
returns, ensuring each header includes RorkHookTypes.h so the macros are
available.

In `@Sources/RorkHook/RorkHookMemory.c`:
- Around line 135-147: The memcpy may cross a page boundary when slot is within
sizeof(void*) of the page end, so update the write-protection logic in the block
that computes slotSize, page and calls RorkHookProtectMemory: compute slotEnd
and pageEnd to detect spansPages (using slot, slotSize, vm_page_size), if spans
protect both pages (e.g. call RorkHookProtectMemory on page and pageEnd or use
combined size) and ensure you track and later restore both original protections
(use RorkHookProtectionForPage for each page and the existing restoreReadOnly/
RorkHookAllowTPROWrites fallback path), then only perform memcpy +
sys_dcache_flush after both pages are writable and restore protections for both
pages afterwards.

In `@Tests/RorkHookTests/RorkHookEnvironmentTests.swift`:
- Around line 35-37: The assertions using path.utf8CString and path.utf8 are
tautological; instead, keep the call to RorkHookLocateSharedCache() and inspect
the raw C pointer cPath directly to verify NUL-termination and absence of
interior NULs: replace the XCTAssertEqual(path.utf8CString.last, 0) and
XCTAssertFalse(path.utf8.contains(0)) checks with assertions that read bytes
from cPath (e.g. iterate cPath until the first NUL and assert that that
terminating byte is 0 and that no earlier byte equals 0), or assert a specific
terminating index byte is 0 and bytes before it are non-zero, referencing
RorkHookLocateSharedCache and the cPath pointer to locate the code to change.

---

Nitpick comments:
In `@Sources/RorkHook/RorkHookArm64.c`:
- Around line 24-40: The mask in RorkHookDecodeADDImmediate currently includes
the sh bit (bit 22), making the extracted shift always zero; to support both
unshifted and LSL#12 forms, relax the compare mask from 0xffc00000u to
0xff800000u (i.e., clear bit 22 in the mask) so the check ((instruction &
0xff800000u) != 0x91000000u) allows either sh=0 or sh=1, keep the existing shift
extraction ((instruction >> 22) & 0x3u) and the offset computation, or if you
intend only the unshifted form, remove the unreachable shift/LSL#12 branch and
simplify to *offset = imm12 after keeping the original mask.
- Around line 103-111: The check in RorkHookFollowOneBranch currently uses mask
0x7c000000 which doesn't include bit 31 and therefore matches both B and BL; to
match only unconditional B, change the mask to include bit 31 (e.g. replace
0x7c000000u with 0xFC000000u) so the comparison (instruction & 0xFC000000u) ==
0x14000000u only succeeds for B; keep the rest of the function (including the
use of RorkHookArm64SignExtend) unchanged.

In `@Sources/RorkHook/RorkHookInternal.c`:
- Around line 127-153: Duplicate vm_region_64 lookup logic in
RorkHookSignPointerIfExecutable and RorkHookMemoryIsReadable should be extracted
into a small helper (e.g., RorkHookQueryRegionProtection) that takes a pointer,
performs vm_region_64 and mach_port_deallocate, and returns whether the region
was found plus its protection bits; then update RorkHookSignPointerIfExecutable
to call that helper and test VM_PROT_EXECUTE, and update
RorkHookMemoryIsReadable to call the same helper and test the readable bit, so
both call sites share the same region-query implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7b40951f-ac75-48aa-ad1f-ccf1a650c194

📥 Commits

Reviewing files that changed from the base of the PR and between 002672d and 88a8266.

📒 Files selected for processing (33)
  • .github/workflows/ci.yml
  • .gitignore
  • CHANGELOG.md
  • Docs/Architecture.md
  • Docs/Release.md
  • Docs/Safety.md
  • NOTICE.md
  • Package.swift
  • README.md
  • Scripts/smoke-client-package.sh
  • Sources/RorkHook/RorkHookArm64.c
  • Sources/RorkHook/RorkHookFunction.c
  • Sources/RorkHook/RorkHookInternal.c
  • Sources/RorkHook/RorkHookMemory.c
  • Sources/RorkHook/RorkHookRebind.c
  • Sources/RorkHook/RorkHookSharedCache.c
  • Sources/RorkHook/RorkHookSymbols.c
  • Sources/RorkHook/RorkHookVersion.c
  • Sources/RorkHook/include/RorkHook.h
  • Sources/RorkHook/include/RorkHook/RorkHook.h
  • Sources/RorkHook/include/RorkHookArm64.h
  • Sources/RorkHook/include/RorkHookFunction.h
  • Sources/RorkHook/include/RorkHookMemory.h
  • Sources/RorkHook/include/RorkHookRebind.h
  • Sources/RorkHook/include/RorkHookSharedCache.h
  • Sources/RorkHook/include/RorkHookSymbols.h
  • Sources/RorkHook/include/RorkHookTypes.h
  • Sources/RorkHook/include/RorkHookVersion.h
  • Sources/RorkHook/include/module.modulemap
  • Sources/RorkHook/private/RorkHookDyldCache.h
  • Sources/RorkHook/private/RorkHookInternal.h
  • Tests/RorkHookTests/RorkHookEnvironmentTests.swift
  • Tests/RorkHookTests/RorkHookFunctionTests.swift

Comment thread .github/workflows/ci.yml Outdated
Comment thread Scripts/smoke-client-package.sh
Comment thread Sources/RorkHook/include/RorkHook.h Outdated
Comment thread Sources/RorkHook/include/RorkHookSymbols.h Outdated
Comment thread Sources/RorkHook/RorkHookMemory.c
Comment thread Tests/RorkHookTests/RorkHookEnvironmentTests.swift Outdated
@mozharovsky mozharovsky force-pushed the codex/prepare-rork-hook-0.1.0 branch 2 times, most recently from 67da2fa to 3f84469 Compare June 1, 2026 14:42
@mozharovsky mozharovsky force-pushed the codex/prepare-rork-hook-0.1.0 branch from 3f84469 to 26e53d3 Compare June 1, 2026 14:49
@mozharovsky mozharovsky self-assigned this Jun 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
Sources/RorkHook/RorkHookMemory.c (1)

135-157: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle cross-page pointer writes in RorkHookStoreProtectedPointer.

At Line 136 and Line 146, only the first page is reprotected, but the pointer write can cross into the next page when slot is near a page boundary. That can fail the write or fault on hardened/read-only mappings.

💡 Minimal fix sketch
-    vm_address_t page = (vm_address_t)((uintptr_t)slot & ~(uintptr_t)(vm_page_size - 1));
-    vm_prot_t originalProtection = RorkHookProtectionForPage(page);
-    kern_return_t result = RorkHookProtectMemory(page,
-                                                 vm_page_size,
-                                                 protection);
+    vm_address_t page = (vm_address_t)((uintptr_t)slot & ~(uintptr_t)(vm_page_size - 1));
+    vm_address_t slotEnd = (vm_address_t)((uintptr_t)slot + slotSize - 1);
+    vm_address_t endPage = (vm_address_t)((uintptr_t)slotEnd & ~(uintptr_t)(vm_page_size - 1));
+    bool spansPages = (endPage != page);
+
+    vm_prot_t originalProtection = RorkHookProtectionForPage(page);
+    vm_prot_t originalEndProtection = spansPages ? RorkHookProtectionForPage(endPage) : VM_PROT_READ;
+
+    kern_return_t result = RorkHookProtectMemory(page, vm_page_size, protection);
+    if (result == KERN_SUCCESS && spansPages) {
+        result = RorkHookProtectMemory(endPage, vm_page_size, protection);
+    }
@@
-    kern_return_t restore = RorkHookProtectMemory(page,
-                                                  vm_page_size,
-                                                  originalProtection ? originalProtection : VM_PROT_READ);
-    return restore == KERN_SUCCESS;
+    kern_return_t restore = RorkHookProtectMemory(
+        page, vm_page_size, originalProtection ? originalProtection : VM_PROT_READ);
+    if (spansPages) {
+        kern_return_t restoreEnd = RorkHookProtectMemory(
+            endPage, vm_page_size, originalEndProtection ? originalEndProtection : VM_PROT_READ);
+        return restore == KERN_SUCCESS && restoreEnd == KERN_SUCCESS;
+    }
+    return restore == KERN_SUCCESS;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/RorkHook/RorkHookMemory.c` around lines 135 - 157,
RorkHookStoreProtectedPointer can fail when the target slot spans a page
boundary; compute the end page for the write (e.g., vm_address_t endPage =
(vm_address_t)((((uintptr_t)slot + slotSize - 1) & ~(uintptr_t)(vm_page_size -
1)))); if endPage != page, call RorkHookProtectMemory for endPage as well and
capture its original protection like originalProtectionEnd, and on restore call
RorkHookProtectMemory for both pages (using originalProtection and
originalProtectionEnd or VM_PROT_READ fallback); also ensure sys_dcache_flush
and the AllowTPROWrites logic (restoreReadOnly) still work and that both pages
are restored on early returns or errors (use the same result/restore variables
for both protects).
.github/workflows/ci.yml (1)

15-16: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden checkout step by SHA-pinning and disabling credential persistence.

Line 16 is tag-pinned and checkout credentials are persisted by default. Please pin to a full commit SHA and set persist-credentials: false.

♻️ Proposed fix
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@<FULL_40_CHAR_SHA_FOR_V4>
+        with:
+          persist-credentials: false
#!/usr/bin/env bash
set -euo pipefail

echo "== checkout pinning and credential persistence checks =="
rg -n 'uses:\s*actions/checkout@' .github/workflows/ci.yml
rg -n 'persist-credentials:\s*false' .github/workflows/ci.yml
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 15 - 16, Replace the tag-pinned
checkout step "uses: actions/checkout@v4" with the commit-SHA pinned invocation
and disable credential persistence by adding "persist-credentials: false";
specifically update the workflow step that currently uses actions/checkout@v4 to
use the repository@<full-commit-sha> form and add the persist-credentials: false
key under that step so credentials are not stored in the runner.
Scripts/smoke-client-package.sh (1)

26-33: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Derive SwiftPM package identity from ROOT_DIR instead of hardcoding rork-hook.

With .package(path: "$ROOT_DIR"), hardcoding the package: value can break resolution when the checkout directory basename differs. This still looks unresolved at Line 32 and Line 40.

♻️ Proposed fix
 ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+PACKAGE_ID="$(basename "$ROOT_DIR")"
+PACKAGE_ID="${PACKAGE_ID%.git}"
+PACKAGE_ID="$(printf '%s' "$PACKAGE_ID" | tr '[:upper:]' '[:lower:]')"
 SMOKE_DIR="${TMPDIR:-/tmp}/rork-hook-client-smoke"
 IOS_DERIVED_DATA="${TMPDIR:-/tmp}/rork-hook-client-smoke-ios-dd"
@@
         .target(
             name: "ObjCSmoke",
             dependencies: [
-                .product(name: "RorkHook", package: "rork-hook"),
+                .product(name: "RorkHook", package: "$PACKAGE_ID"),
             ],
             publicHeadersPath: "include"
         ),
@@
             dependencies: [
                 "ObjCSmoke",
-                .product(name: "RorkHook", package: "rork-hook"),
+                .product(name: "RorkHook", package: "$PACKAGE_ID"),
             ]
         ),
#!/usr/bin/env bash
set -euo pipefail

# Verify hardcoded package identity is gone and computed identity exists.
rg -n 'PACKAGE_ID=' Scripts/smoke-client-package.sh
rg -n '\.product\(name:\s*"RorkHook",\s*package:\s*"\$PACKAGE_ID"\)' Scripts/smoke-client-package.sh
rg -n '\.product\(name:\s*"RorkHook",\s*package:\s*"rork-hook"\)' Scripts/smoke-client-package.sh && exit 1 || true

Also applies to: 40-41

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Scripts/smoke-client-package.sh` around lines 26 - 33, Replace the hardcoded
SwiftPM package identity "rork-hook" by deriving it from ROOT_DIR: compute a
PACKAGE_ID (e.g., basename of ROOT_DIR) early in the script and then use that
variable in the Swift package manifest snippets so both .product(name:
"RorkHook", package: ...) occurrences reference package: "$PACKAGE_ID" instead
of "rork-hook"; update the two occurrences shown (the .product lines near the
.package(path: "$ROOT_DIR") and the other instance around lines 40-41) to use
the PACKAGE_ID variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/RorkHook/RorkHookRebind.c`:
- Around line 36-86: The code only changes protection for the single page
computed as page, but the write "*slot = value" can cross into the next page;
update RorkHookStoreSlot to detect if the write spans a page boundary (e.g., if
((uintptr_t)slot & (vm_page_size - 1)) + sizeof(void*) > vm_page_size), compute
the second page address (page2 = page + vm_page_size), and apply the same
protection changes (RorkHookProtectMemory) to both pages before writing and
restore original protections on both pages afterwards; ensure TPRO handling
(RorkHookSupportsTPRO, RorkHookThreadCanWriteTPRO, RorkHookBeginThreadTPROWrite,
RorkHookEndThreadTPROWrite) and the sys_dcache_flush call are applied correctly
when the second page is involved and deallocate any mach ports returned by
vm_region_64 for both regions.

---

Duplicate comments:
In @.github/workflows/ci.yml:
- Around line 15-16: Replace the tag-pinned checkout step "uses:
actions/checkout@v4" with the commit-SHA pinned invocation and disable
credential persistence by adding "persist-credentials: false"; specifically
update the workflow step that currently uses actions/checkout@v4 to use the
repository@<full-commit-sha> form and add the persist-credentials: false key
under that step so credentials are not stored in the runner.

In `@Scripts/smoke-client-package.sh`:
- Around line 26-33: Replace the hardcoded SwiftPM package identity "rork-hook"
by deriving it from ROOT_DIR: compute a PACKAGE_ID (e.g., basename of ROOT_DIR)
early in the script and then use that variable in the Swift package manifest
snippets so both .product(name: "RorkHook", package: ...) occurrences reference
package: "$PACKAGE_ID" instead of "rork-hook"; update the two occurrences shown
(the .product lines near the .package(path: "$ROOT_DIR") and the other instance
around lines 40-41) to use the PACKAGE_ID variable.

In `@Sources/RorkHook/RorkHookMemory.c`:
- Around line 135-157: RorkHookStoreProtectedPointer can fail when the target
slot spans a page boundary; compute the end page for the write (e.g.,
vm_address_t endPage = (vm_address_t)((((uintptr_t)slot + slotSize - 1) &
~(uintptr_t)(vm_page_size - 1)))); if endPage != page, call
RorkHookProtectMemory for endPage as well and capture its original protection
like originalProtectionEnd, and on restore call RorkHookProtectMemory for both
pages (using originalProtection and originalProtectionEnd or VM_PROT_READ
fallback); also ensure sys_dcache_flush and the AllowTPROWrites logic
(restoreReadOnly) still work and that both pages are restored on early returns
or errors (use the same result/restore variables for both protects).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78ff5033-e385-441c-b699-e88ac7e1e2c2

📥 Commits

Reviewing files that changed from the base of the PR and between 88a8266 and 3f84469.

📒 Files selected for processing (39)
  • .github/workflows/ci.yml
  • .gitignore
  • CHANGELOG.md
  • Docs/Architecture.md
  • Docs/Release.md
  • Docs/Safety.md
  • NOTICE.md
  • Package.swift
  • README.md
  • Scripts/smoke-client-package.sh
  • Sources/RorkHook/RorkHookArm64.c
  • Sources/RorkHook/RorkHookFunction.c
  • Sources/RorkHook/RorkHookInternal.c
  • Sources/RorkHook/RorkHookMemory.c
  • Sources/RorkHook/RorkHookRebind.c
  • Sources/RorkHook/RorkHookSharedCache.c
  • Sources/RorkHook/RorkHookSymbols.c
  • Sources/RorkHook/RorkHookVersion.c
  • Sources/RorkHook/include/RorkHook.h
  • Sources/RorkHook/include/RorkHook/RorkHook.h
  • Sources/RorkHook/include/RorkHookArm64.h
  • Sources/RorkHook/include/RorkHookFunction.h
  • Sources/RorkHook/include/RorkHookMemory.h
  • Sources/RorkHook/include/RorkHookRebind.h
  • Sources/RorkHook/include/RorkHookSharedCache.h
  • Sources/RorkHook/include/RorkHookSymbols.h
  • Sources/RorkHook/include/RorkHookTypes.h
  • Sources/RorkHook/include/RorkHookVersion.h
  • Sources/RorkHook/include/module.modulemap
  • Sources/RorkHook/private/RorkHookDyldCache.h
  • Sources/RorkHook/private/RorkHookInternal.h
  • Tests/RorkHookTestSupport/RorkHookTestSupport.m
  • Tests/RorkHookTestSupport/include/RorkHookTestSupport.h
  • Tests/RorkHookTests/RorkHookArm64Tests.swift
  • Tests/RorkHookTests/RorkHookEnvironmentTests.swift
  • Tests/RorkHookTests/RorkHookFunctionTests.swift
  • Tests/RorkHookTests/RorkHookMemoryTests.swift
  • Tests/RorkHookTests/RorkHookRebindTests.swift
  • Tests/RorkHookTests/RorkHookSymbolTests.swift
✅ Files skipped from review due to trivial changes (9)
  • Sources/RorkHook/include/RorkHookVersion.h
  • Tests/RorkHookTests/RorkHookRebindTests.swift
  • Sources/RorkHook/include/module.modulemap
  • Sources/RorkHook/include/RorkHook/RorkHook.h
  • Docs/Release.md
  • NOTICE.md
  • Docs/Safety.md
  • README.md
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (14)
  • Sources/RorkHook/include/RorkHook.h
  • Sources/RorkHook/RorkHookVersion.c
  • Sources/RorkHook/include/RorkHookSymbols.h
  • Sources/RorkHook/include/RorkHookRebind.h
  • Tests/RorkHookTests/RorkHookEnvironmentTests.swift
  • Sources/RorkHook/include/RorkHookArm64.h
  • Package.swift
  • Sources/RorkHook/include/RorkHookFunction.h
  • Sources/RorkHook/private/RorkHookInternal.h
  • Tests/RorkHookTests/RorkHookFunctionTests.swift
  • Sources/RorkHook/RorkHookSymbols.c
  • Sources/RorkHook/RorkHookArm64.c
  • Sources/RorkHook/private/RorkHookDyldCache.h
  • Sources/RorkHook/include/RorkHookMemory.h

Comment thread Sources/RorkHook/RorkHookRebind.c Outdated
@mozharovsky mozharovsky merged commit c0d8699 into main Jun 1, 2026
2 checks passed
@mozharovsky mozharovsky deleted the codex/prepare-rork-hook-0.1.0 branch June 1, 2026 15:19
Sign up for free to 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