Post

Replies

Boosts

Views

Activity

Reply to Sanboxed Apps Reading Extended Security Information (ACL)
SO, the question here is how/why an ACL was set on the file but the xattr was not created. The ACL was set on the file with chmod(1): % chmod +a 'developer allow write,append,delete' /Volumes/myfs/nf.txt In any case, the real key point is here: Next, the ACL extracted from the source file sec object is applied to the brand-new file sec object created via CFFileSecuritySetAccessControlList(newFileSec,tmpACL): At the point this succeeded, the file should have acquired a "com.apple.system.Security" attribute, since that's where your file system should be storing the ACL. Why didn't it? Actually, it did. The output I supplied was meant to demonstrate it, 0xaf54acd10 being the new file sec object: <FileSecurity 0xaf54acd10> [0xaf6a71980] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_MODE = 33060, FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = !#acl 1 user:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append } I guess I should've made it explicitly clear that CFFileSecuritySetAccessControlList(newFileSec,tmpACL) did succeed. That matters because the reason copyExtendedAttribute did this first: Calls getxattr(dstFile, 'com.apple.system.Security', NULL, 0, 0, XATTR_NOFOLLOW) to obtain the size of the attribute. -1 is returned. ...is because IF an xattr had been returned there, as it should have been, copyExtendedAttribute would have then skipped this step: Calls getxattr(srcFile, 'com.apple.system.Security', buffer, 512, 0, XATTR_NOFOLLOW) to obtain the ACL EA. -1 is returned with errno==1 EPERM. ...and simply continued normally without any error. Your statement about copyExtendedAttribute skipping these getxattr(2) calls isn't accurate. I'll expand on that later. Both getxattr(2) calls fail with EPERM because com.apple.system.Security is a protected EA and the TextEdit process is running with regular privileges and it doesn't have FILESEC_ACCESS_ENTITLEMENT, getxattr(2): if (xattr_protected(attrname) && (error = xattr_entitlement_check(attrname, ctx, false)) != 0) { goto out; } I dynamically analysed these functions, URLReplaceObject,CFURLCopySecurityEAs,TransferExtendedAttributes,copyExtendedAttribute, for APFS to see which code paths are taken on a successful run. The file resides on APFS: % df -h apfs.txt Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/disk4s5 367Gi 105Gi 241Gi 31% 1.0M 2.5G 0% /System/Volumes/Data And uses the following POSIX and ACL permissions: % ls -le apfs.txt -r--r--r--+ 1 developer staff 6 Sep 28 16:20 apfs.txt 0: user:developer allow write,delete,append The ACL permissions on apfs.txt were created with chmod(1): % chmod +a 'developer allow write,append.delete' apfs.txt I found that the code behaves as I had observed with my filesystem, i.e.: A new file sec object is created. The ACL is obtained from the source file's file sec object. The ACL is stored in the new file sec object. The new file sec object is stored in a dictionary CFDictionaryAddValue(dict,NSURLFileSecurityKey,newFileSec) for further use. TransferExtendedAttributes(alloc,srcURL,dstURL,&error) gets called for APFS as well. Because the file had no EAs created by TextEdit yet, later on com.apple.TextEncoding gets added for this file by TextEdit, but that has no bearing on the ACL situation. TransferExtendedAttributes calls listxattr(2) which returns zero. TransferExtendedAttributes returns two values to the caller: The status code returned is 1, probably a Boolean value and The CFError parameter is set to NULL on success. This is where the behaviour differs between my filesystem and APFS. My filesystem returns 0 in the status code and a non-null CFError. On TransferExtendedAttributes returning success for APFS, the actual call that writes the new file sec object out to disk is: CFURLSetResourcePropertiesForKeys(dstURL,dict,&error): dstURL: file:///Users/developer/Library/Containers/com.apple.TextEdit/Data/tmp/TemporaryItems/NSIRD_TextEdit_EfokSZ/apfs.txt dict: { NSURLCreationDateKey = "2026-09-28 13:03:50 +0000"; NSURLFileSecurityKey = "<FileSecurity 0xc289c1f50> [0xc28aec680] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_M ODE = 33060, FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = !#acl 1\nuser:ED27D762-80A1-496B-BD81-4AE8CE54CE82: developer:501:allow:write,delete,append\n}"; } And this is it. To see if this would result in the same code path being taken for my filesystem, I modified the status code and the CFError object returned by TransferExtendedAttributes dynamically to 1 and NULL respectively while debugging with Ghidra and lldb, and had CFURLSetResourcePropertiesForKeys(dstURL,dict,&error) called with the file contents being successfully written out to disk. To the best of my knowledge, this brings us back to my earlier assumption, which has now been confirmed by these findings, that TextEdit and the like aren't permitted to query com.apple.system.Security. The only way to have TextEdit interoperate with ACLs is to have them stored as file metadata instead of as an extended attribute.
Topic: App & System Services SubTopic: Core OS Tags:
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
To better understand the situation I resorted to dynamic analisys of the following CoreServicesInternal functions using Ghidra, a software reverse engineering tool: URLReplaceObject CFURLCopySecurityEAs TransferExtendedAttributes copyExtendedAttribute The following test case was considered: Source file mode: 0444 Source file ACL: user id 501 allow write,append,delete The temporary file, created by TextEdit, i.e. the destination file mode: 0644 srcUrlMode != dstUrlMode Execution path CFURLCopySecurityEAs(srcURL,dstURL,&local_8d0) This function is called based on xpc_runtime_is_app_sandboxed() returning true and isSrcURLDirectory==isDstURLDirectory. Internally, CFURLCopySecurityEAs(): Opens both srcURL and dstURL Retrieves a list of EAs from srcURL via flistxattr(2): % sudo xattr /Volumes/myfs/nf.txt com.apple.quarantine com.apple.TextEncoding com.apple.lastuseddate#PS com.apple.system.Security Iterates over the list of EAs returned Copies any EAs that match the com.apple.security. pattern, e.g.: % kextstat 2>/dev/null | awk '$6~/com.apple.security./{print $6}' com.apple.security.AppleImage4 com.apple.security.sandbox com.apple.security.quarantine com.apple.security.BootPolicy The value returned is 1. Next, the file security object associated with the source and destination URLs is obtained via CFURLCopyResourcePropertyForKey: CFURLCopyResourcePropertyForKey(srcURL,NSURLFileSecurityKey,&srcFileSec,&error); CFURLCopyResourcePropertyForKey(dstURL,NSURLFileSecurityKey,&dstFileSec,&error); Next, a new file security object is created via CFFileSecurityCreate: <FileSecurity 0xaf54acd10> [0xaf6a71980] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_MODE = (null), FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = (null)} Next, the file mode 33060==o100444 is extracted from srcFileSec via CFFileSecurityGetMode(srcFileSec,&srcUrlMode): <FileSecurity 0xa3f5dee60> [0xa3fbf0a80] {FILESEC_OWNER = 501, FILESEC_GROUP = 20, FILESEC_MODE = 33060, FILESEC_UUID = 0000000 0-0000-0000-0000-000000000000, FILESEC_GRPUUID = 00000000-0000-0000-0000-000000000000, FILESEC_ACL = !#acl 1 user:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append } Next, the GID 20 (staff) of the owner of the source and the destination files is extracted from srcFileSec and dstFileSec respectively via CFFileSecurityGetGroup: CFFileSecurityGetGroup(srcFileSec,(long)&srcgid) CFFileSecurityGetGroup(dstFileSec,&dstgid) Next, the mode of the source file is applied to the brand new file sec object via CFFileSecuritySetMode(newFileSec,srcUrlMode): <FileSecurity 0xaf54acd10> [0xaf6a71980] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_MODE = 33060, FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = (null)} Next, the ACL is extracted from the source file sec object via CFFileSecurityCopyAccessControlList(srcFileSec,&tmpACL): <FileSecurity 0xa3f5dee60> [0xa3fbf0a80] {FILESEC_OWNER = 501, FILESEC_GROUP = 20, FILESEC_MODE = 33060, FILESEC_UUID = 0000000 0-0000-0000-0000-000000000000, FILESEC_GRPUUID = 00000000-0000-0000-0000-000000000000, FILESEC_ACL = !#acl 1 user:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append } (lldb) expr char* $tmpACL = (char*)acl_to_text(*(void**)0x16d35a138, (long*)0) (lldb) p (char*)$tmpACL (lldb) "!#acl 1\nuser:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append\n" Next, the ACL extracted from the source file sec object is applied to the brand new file sec object created via CFFileSecuritySetAccessControlList(newFileSec,tmpACL): <FileSecurity 0xaf54acd10> [0xaf6a71980] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_MODE = 33060, FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = !#acl 1 user:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append } Next, the destination file mode o100644 is extracted from the destination file sec object via CFFileSecurityGetMode(dstFileSec,&dstUrlMode): <FileSecurity 0xa3f5def70> [0xa3fbf0200] {FILESEC_OWNER = 501, FILESEC_GROUP = 20, FILESEC_MODE = 33188 (o100644), FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = (null)} Next, the brand new file sec object is added to a dictionary: CFDictionaryAddValue(dict,NSURLFileSecurityKey,newFileSec) Next, TransferExtendedAttributes(alloc,srcURL,dstURL,&error) is called. Internally, TransferExtendedAttributes: Obtains a list of EAs via listxattr(2) Calls xattr_preserve_for_intent for each of the EAs iteratively passing XATTR_OPERATION_INTENT_SAVE. Calls copyExtendedAttribute to copy the ACL EA from the source to the destination file. xattr_preserve_for_intent considers the following EAs worth saving: com.apple.lastuseddate#PS com.apple.system.Security The EAs are saved by calling copyExtendedAttribute. com.apple.lastuseddate#PS is saved successfully. com.apple.system.Security fails to be saved. Internally, copyExtendedAttribute: Calls getxattr(dstFile, 'com.apple.system.Security', NULL, 0, 0, XATTR_NOFOLLOW) to obtain the size of the attribute. -1 is returned. Calls getxattr(srcFile, 'com.apple.system.Security', buffer, 512, 0, XATTR_NOFOLLOW) to obtain the ACL EA. -1 is returned with errno==1 EPERM. TransferExtendedAttributes returns 0 to the caller and the errno in the error parameter. URLReplaceObject proceeds down the error path and ends up setting a CFError to: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “nf.txt.sb-4b9a8883-7jQtqz” in the folder “myfs”." UserInfo={NSURL=file:///Volumes/myfs/nf.txt.sb-4b9a8883-7jQtqz, NSFilePath=/Volumes/myfs/nf.txt.sb-4b9a8883-7jQtqz, NS UnderlyingError=0xaf76952c0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}} URLReplaceObject returns 0x201==513 to the caller. If you have any further guidance to offer, I would greatly appreciate it.
Topic: App & System Services SubTopic: Core OS Tags:
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
Are you seeing an ACL get on the source? Or the ACL set on the destination? I was able to map this syscall returning ENOATTR: 2 => getxattr('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', value:0, size:0, position:0, options:1) TextEdit-3772 3 <= getxattr('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', value:0, size:0, position:0, options:1) -> -1 (93) TextEdit-3772 ;ENOATTR To this vnop_getxattr call in my kext, with args->a_size=0 returned: vnop_getxattr: TextEdit-3772 -> getxattr_rpc('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', fffffe2fbb998180, uio_resid(EAvalue):762) vnop_getxattr: TextEdit-3772 <- getxattr_rpc('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', fffffe2fbb998180, uio_resid(EAvalue):762) -> 93 ;ENOATTR vnop_getxattr: TextEdit-3772 '/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt' 'com.apple.system.Security' *args->a_size=0 vnop_getxattr: TextEdit-3772 '/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt' 'com.apple.system.Security' <- 93 ;ENOATTR The next syscall succeeds in returning the com.apple.system.Security EA, with the EA size of 512 bytes returned. This differs from the size returned by my kext being 68 bytes. See below: 3 => getxattr('/Volumes/myfs/f.txt', 'com.apple.system.Security', value:16de4d390, size:512, position:0, options:1) TextEdit-3772 0 <= getxattr('/Volumes/myfs/f.txt', 'com.apple.system.Security', value:16de4d390, size:512, position:0, options:1) -> 512 (0) TextEdit-3772 vnop_getxattr: TextEdit-3772 -> getxattr_rpc('/Volumes/myfs/f.txt', 'com.apple.system.Security', fffffecff9e3ba80, uio_resid(EAvalue):512) vnop_getxattr: TextEdit-3772 <- getxattr_rpc('/Volumes/myfs/f.txt', 'com.apple.system.Security', fffffecff9e3ba80, uio_resid(EAvalue):488) -> 68 vnop_getxattr: TextEdit-3772 '/Volumes/myfs/f.txt' 'com.apple.system.Security' *args->a_size=68 vnop_getxattr: TextEdit-3772 '/Volumes/myfs/f.txt' 'com.apple.system.Security' <- 0 ; KERN_SUCCESS This syscall never calls into the vnop_setxattr in my kext: 0 => setxattr('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', value:16de4d390, size:512, position:0, options:1) TextEdit-3772 0 <= setxattr('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', value:16de4d390, size:512, position:0, options:1) -> -1 (1) TextEdit-3772 ;EPERM EPERM possibly being returned from setxattr via the following execution path: if (xattr_protected(sactx->attrname) && (error = xattr_entitlement_check(sactx->attrname, ctx, true)) != 0) { goto out; }
Topic: App & System Services SubTopic: Core OS Tags:
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
[quote='904084022, DTS Engineer, /thread/843772?answerId=904084022#904084022'] What are the permissions of the parent directory, and do they allow object creation? [/quote] The parent directory that houses the test file is the root of the filesystem which uses the most liberal POSIX permissions and has no ACL permissions set: % ls -led /Volumes/myfs/ drwxrwxrwx 8 root wheel 15 Sep 4 16:53 /Volumes/myfs/ I've been able to identify the point of failure in TextEdit using dtrace(1) via the syscall provider. When replacing the original file, /Volumes/myfs/f.txt, with the temporary copy, TextEdit queries the presence of the com.apple.system.Security EA in the copy, /Volumes/myfs/f.txt.sb-1f18ed33-4luthh/f.txt. It gets ENOATTR returned, because the attribute isn't there. Next, the original file is queried to get the size of the EA. Finally, an attempt is made to create the security EA for the copy. It's this call that returns EPERM. Extended attributes are queried and set using the following execution path, NSDocument saveToURL -> NSDocument writeSafelyToURL -> NSFileManager replaceItemAtURL -> CoreServicesInternal _URLReplaceObject -> CoreServicesInternal TransferExtendedAttributes -> libsystem_kernel.dylib getxattr or libsystem_kernel.dylibsetxattr` See the file attached, dtrace-textedit-xattr.txt, for the diagnostics emitted by dtrace(1). dtrace-textedit-xattr.txt I guess, the only options available to me are, apps like TextEdit, Pages and possibly some other apps like that as well shouldn't be used to modify files when ACLs are stored as the system security EA, or the ACL implementation in my filesystem kext should be redone using the ATTR_CMN_EXTENDED_SECURITY VFS file metadata attribute.
Topic: App & System Services SubTopic: Core OS Tags:
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
Is it possible that YOU ended up calling "getxattr" yourself as part of the permission check? No, I do not call getxattr explicitly. Both setxattr and getxattr are called by the system and my filesystem just stores and retrieves the ACL EA from the server. I've built a test app with the following entitlements enabled: % codesign -d --entitlements - --xml SandboxedFileEditor.app | plutil -p - Executable=SandboxedFileEditor.app/Contents/MacOS/SandboxedFileEditor { "com.apple.developer.applesignin" => { "com.apple.security.app-sandbox" => true "com.apple.security.files.bookmarks.app-scope" => true "com.apple.security.files.user-selected.read-write" => true } "com.apple.security.app-sandbox" => true "com.apple.security.files.user-selected.read-write" => true "com.apple.security.get-task-allow" => true } I was able to successfully modify and save the contents of the test file residing on my custom filesystem: % cat /Volumes/myfs/f.txt line1 line2 With the following POSIX and ACL permissions set: % ls -le /Volumes/myfs/f.txt -r--r--r--@ 1 user staff 6 Sep 3 17:55 /Volumes/myfs/f.txt 0: user:user allow write,delete,append And the ACL being stored as a com.apple.system.Security EA: % sudo xattr /Volumes/myfs/f.txt com.apple.TextEncoding com.apple.quarantine com.apple.system.Security I still don't understand how to debug TextEdit and Pages not being permitted to save the modified file contents. In the system log, I've found the following error message: 2026-08-14 16:50:41.313445+0300 0x2718a Error 0x62db7 442 0 Finder: (Foundation) [com.apple.foundation.filecoordination:claims] 73600188-34F7-4F6C-A697-34A74474169C grantAccessClaim reply is an error: Error Domain=NSCocoaErrorDomain Code=513 "The document “f1.txt” could not be autosaved. You don’t have permission." UserInfo={NSUnderlyingError=0x83891b900 {Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “f1.txt” in the folder “." UserInfo={NSURL=file:///Volumes/myfs/f1.txt, NSUnderlyingError=0x83891b8d0 {Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “f1.txt.sb-23e6090e-qqHT2x” in the folder “." UserInfo={NSURL=file:///Volumes/myfs/f1.txt.sb-23e6090e-qqHT2x, NSFilePath=/Volumes/myfs/f1.txt.sb-23e6090e-qqHT2x, NSUnderlyingError=0x83891bc00 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}}}}, NSLocalizedDescription=The document “f1.txt” could not be autosaved. You don’t have permission., NSURL=file:///Volumes/myfs/f1.txt, NSLocalizedFailure Do you have any further suggestions to make in this respect?
Topic: App & System Services SubTopic: Core OS Tags:
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
Thanks for your response. When evaluating KAUTH_FILESEC_XATTR via getxattr(), xattr_entitlement_check() succeeds if the calling process is that of the superuser, or if it carries the FILESEC_ACCESS_ENTITLEMENT entitlement. TextEdit does not cary the FILESEC_ACCESS_ENTITLEMENT: % codesign -d --entitlements - --xml /System/Applications/TextEdit.app | plutil -p - Executable=/System/Applications/TextEdit.app/Contents/MacOS/TextEdit { "com.apple.application-identifier" => "com.apple.TextEdit" "com.apple.developer.ubiquity-container-identifiers" => [ 0 => "com.apple.TextEdit" ] "com.apple.private.hid.client.event-dispatch.internal" => true "com.apple.security.app-sandbox" => true "com.apple.security.files.user-selected.executable" => true "com.apple.security.files.user-selected.read-write" => true "com.apple.security.print" => true } This explains the behaviour I'm observing, i.e. running TextEdit as a regular user fails to write file changes and returning EPERM, and running TextEdit as a superuser succeeds in writing the file contents. xattr(1) behaves the same way: % ls -le /Volumes/myfs/f.txt -r--r--r--@ 1 user staff 0 Aug 27 18:39 /Volumes/myfs/f.txt 0: user:user allow write,delete,append % xattr /Volumes/myfs/f.txt com.apple.TextEncoding xattr: [Errno 1] Operation not permitted: '/Volumes/myfs/f.txt' % sudo xattr /Volumes/myfs/f.txt com.apple.TextEncoding com.apple.system.Security What I need clarified is this, if my custom filesystem kext handles ACL data as the KAUTH_FILESEC_XATTR, which it does in the current implementation, then sandboxed apps won't be able to save modified file contents, because they fail the xattr_entitlement_check(). Is my only option is this case to handle ACL data through ATTR_CMN_EXTENDED_SECURITY? Which the PoC I wrote confirmed as working.
Topic: App & System Services SubTopic: Core OS Tags:
Aug ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
For the sake of completeness, I thought I'd mention that I have been able to debug this by using the Quarantine Queue coupled with Delayed Deallocation, a technique employed by address sanitisers. It works by retaining objects to be released and placing them onto a quarantine queue of a finite size, overwriting the entire object's memory with a canary value like 0xdb or similar to cause a system fault when accessed. When the quarantine queue fills up, it's drained below the set threshold by releasing the memory back to the system.
Topic: App & System Services SubTopic: Core OS Tags:
Aug ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
I've done some further research into a possibility of building and linking a custom kext against the kasan kernel. I've put the following CFLAGS into my Xcode project based on the MakeInc.def Apple's own kasan kernels are built with. OTHER_CFLAGS = -DKASAN_TBI=1 -DKASAN_SCALE=4 -fsanitize=kernel-hwaddress -fsanitize-ignorelist=$(SRCROOT)/../../../../xnu/san/memory/kasan-denylist-arm64 -mllvm -hwasan-recover=0 -mllvm -hwasan-mapping-offset=0xf000000000000000 -mllvm -hwasan-instrument-atomics=1 -mllvm -hwasan-instrument-stack=1 -mllvm -hwasan-generate-tags-with-calls=1 -mllvm -hwasan-instrument-with-calls=0 -mllvm -hwasan-use-short-granules=0 -mllvm -hwasan-memory-access-callback-prefix="__asan_" Xcode emitted the following error error: unsupported option '-fsanitize=kernel-hwaddress' for target 'arm64-apple-macos26.0'. According to this post, https://discourse.llvm.org/t/hw-address-sanitizer-on-arm64-apple-darwin/76153/5, on the llvm support forum, the Xcode toolchain doesn't support kernel address sanitisation: Yes, they have it in XNU. But if HWASan is enabled, it’s not compilable. By default, the clang from XCode is used, so it’s Apple’s own. Tried with apple’s fork of llvm-project which is actively maintained but it’s also unable to compile for target arm64-apple-darwin. If want to experiment, here is a useful all-in-one script that does building, all you need to change is the KERNEL_CONFIG to KASAN. Do I understand it correctly that any further attempts to build and link my custom kext with address sanitisation enabled are futile and should be abandoned?
Topic: App & System Services SubTopic: Core OS Tags:
Jul ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
I've managed to boot a KASAN kernel on Apple Silicon running macOS 15.7.4 build 24G517 by following the instructions, https://kernelshaman.blogspot.com/2021/02/building-xnu-for-macos-112-intel-apple.html, you referneced in a reply above. I was also able to build my custom KEXT with these CFLAGS='-fsanitize=address -mllvm -asan-globals=0' as done by the XNU unit tests: ifeq ($(BUILD_ASAN),1) BUILD_SANITIZERS = 1 # compile XNU with asan # TODO: enable globals instrumentation and write a proper ignorelist for problematic global vars XNU_CFLAGS_EXTRA += -fsanitize=address -mllvm -asan-globals=0 # make mocks code aware of sanitizers runtime being linked SANITIZERS_FLAGS += -D__BUILDING_WITH_ASAN__=1 endif # BUILD_ASAN Attempting to build a boot collection with my asan-instrumented KEXT results in a bunch of undefined asan symbols errors emitted. How do I build my KEXT against a KASAN kernel so I can debug the UAFs it has? Thanks.
Topic: App & System Services SubTopic: Core OS Tags:
Jul ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
You're right about the kernel clearing the thread state I set up via those debug registers. One place this happens in is the machine_thread_set_state function where the monitor debug system control register gets cleared and the debug watchpoint control registers and the debug watchpoint value registers get reset. Thanks for looking into this. I won't be filing a bug for this. I've instrumented the memory allocation call sites with diagnostics to output the allocation size and the data type to match that against the info on the memory that got corrupted by a UAF given in the kernel panic log. I'm now endeavouring to manually track down the code paths that lead to the UAFs.
Topic: App & System Services SubTopic: Core OS Tags:
Jul ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
I've been exploring the possibility of debugging UAFs by setting up hardware watchpoints via the CPU debug registers on Apple Silicon. In particular, I've set up the MDE and KDE bits in the MDSCR_EL1 register, enabled the relevant bits in the DBGWCR register to track both load and store operations, and set up the DBGWVR register to track memory addresses of interest. I have a consistent UAF reproducer, and my custom KEXT I've instrumented via inline assembly doesn't seem to detect memory IO after it's been free'd. So I was wondering, if it's down to me not being aware of some other CPU registers that may need setting up or is it because the system enforces some restrictions on 3rd party KEXTs attempting to manipulate those debug registers. Your clarifying this would be greatly appreciated.
Topic: App & System Services SubTopic: Core OS Tags:
Jun ’26
Reply to Symbolicating kernel backtraces on Apple Silicon
Based on the symbolicated backtraces, there are no references to where the memory is being used after being freed, or where it's being freed. There's just this reference to where it's being allocated: ['atos', '-o', '/Users/user/panic/Kernel_Debug_Kit_15.6_build_24G84.dmg_extracted/KDK.pkg/Payload/System/Library/Kernels/kernel.release.t8103.dSYM/Contents/Resources/DWARF/kernel.release.t8103', '-arch', 'arm64e', '-textExecAddress', '0xfffffe0025c50000', '0xfffffe0025cae7c0', '0xfffffe0026558328', '0xfffffe002655e730', '0xfffffe0025d30f08', '0xfffffe0025cc21c0', '0xfffffe002620afe4', '0xfffffe00243f717c', '0xfffffe00243f7110', '0xfffffe00243df12c', '0xfffffe00243df610', '0xfffffe0024408bc0', '0xfffffe0025ee60cc', '0xfffffe0025ecdf08', '0xfffffe0025eceee4', '0xfffffe002635e9b8', '0xfffffe0025e0fff0', '0xfffffe0025c57b88'] panic_trap_to_debugger (in kernel.release.t8103) (debug.c:1400) kalloc_type_distribute_budget (in kernel.release.t8103) (kalloc.c:1343) kmem_init (in kernel.release.t8103) (vm_kern.c:4584) zone_early_scramble_rr (in kernel.release.t8103) (zalloc.c:3220) kalloc_ext (in kernel.release.t8103) (kalloc.c:2520) mprotect (in kernel.release.t8103) (kern_mman.c:1311) 0xfffffe00243f717c 0xfffffe00243f7110 0xfffffe00243df12c 0xfffffe00243df610 0xfffffe0024408bc0 vn_pathconf (in kernel.release.t8103) (vfs_vnops.c:1875) openat_dprotected_internal (in kernel.release.t8103) (vfs_syscalls.c:5127) mknod (in kernel.release.t8103) (vfs_syscalls.c:5469) sendsig (in kernel.release.t8103) (unix_signal.c:626) sleh_synchronous (in kernel.release.t8103) (sleh.c:1267) fleh_synchronous (in kernel.release.t8103) + 24 The unsymbolicated memory addresses come from my KEXT where memory is allocated via OSMalloc(). The object being allocated ends up being stored in an RB tree. I'd like to take you up on your offer to take a look at the full panic log for me if I may. Please find the full panic log attached to this ticket https://feedbackassistant.apple.com/feedback/21231833.
Topic: App & System Services SubTopic: Core OS Tags:
Dec ’25
Reply to Symbolicating kernel backtraces on Apple Silicon
Hello Kevin, I put together a Python script to do a full symbolication based on your instructions. I'm having trouble computing addresses of functions given in the actual stack frames. First off, the symbolication of the frame of the thread that panicked does succeed as the function addresses come precomputed by the system: % ./symbolicate.py TEXT_EXEC 0xfffffe00072d4000 TEXT 0xfffffe0007004000 ktext_exec_base 0xfffffe002c900000 load_address: 0xfffffe002c630000 panicked_thread_faddrs: ['0xfffffe002c95d93c', '0xfffffe002cacd124', '0xfffffe002cacb31c', '0xfffffe002c903b88', '0xfffffe002c95dc08', '0xfffffe002d264898', '0xfffffe002d2700f8', '0xfffffe002caccf80', '0xfffffe002cacb490', '0xfffffe002c903b88', '0xfffffe002cb7f820', '0xfffffe002cba8538', '0xfffffe002cb8fd00', '0xfffffe002cb905c0', '0xfffffe002d05eb5c', '0xfffffe002cacb3a4', '0xfffffe002c903b88', '0x19d82a2b0'] ['atos', '-o', 'Kernel_Debug_Kit_26_build_25A353.dmg_extracted/KDK.pkg/Payload/System/Library/Kernels/kernel.release.t8103.dSYM/Contents/Resources/DWARF/kernel.release.t8103', '-arch', 'arm64e', '-l', '0xfffffe002c630000', '0xfffffe002c95d93c', '0xfffffe002cacd124', '0xfffffe002cacb31c', '0xfffffe002c903b88', '0xfffffe002c95dc08', '0xfffffe002d264898', '0xfffffe002d2700f8', '0xfffffe002caccf80', '0xfffffe002cacb490', '0xfffffe002c903b88', '0xfffffe002cb7f820', '0xfffffe002cba8538', '0xfffffe002cb8fd00', '0xfffffe002cb905c0', '0xfffffe002d05eb5c', '0xfffffe002cacb3a4', '0xfffffe002c903b88', '0x19d82a2b0'] handle_debugger_trap (in kernel.release.t8103) (debug.c:1863) handle_uncategorized (in kernel.release.t8103) (sleh.c:1818) sleh_synchronous (in kernel.release.t8103) (sleh.c:1698) fleh_synchronous (in kernel.release.t8103) + 24 DebuggerTrapWithState (in kernel.release.t8103) (debug.c:830) Assert (in kernel.release.t8103) (debug.c:841) sleh_synchronous_sp1 (in kernel.release.t8103) (sleh.c:1191) handle_kernel_abort (in kernel.release.t8103) (sleh.c:3960) sleh_synchronous (in kernel.release.t8103) (sleh.c:1698) fleh_synchronous (in kernel.release.t8103) + 24 vn_create (in kernel.release.t8103) (vfs_subr.c:8079) vn_open_auth (in kernel.release.t8103) (vfs_vnops.c:483) open1 (in kernel.release.t8103) (vfs_syscalls.c:0) open_extended (in kernel.release.t8103) (vfs_syscalls.c:5273) unix_syscall (in kernel.release.t8103) (systemcalls.c:181) sleh_synchronous (in kernel.release.t8103) (sleh.c:1484) fleh_synchronous (in kernel.release.t8103) + 24 0x19d82a2b0 Here's the output generated for the very first kernel frame that fails to symbolicate: kernelFrames: tid:110 IOServiceTerminateThread UUID: 8502a040-9cf9-35f5-b8a2-84b0e48d379e [1, 622608] funcaddr: 0xfffffe0008774000+0x98010 -> 0xfffffe000880c010 [1, 617372] funcaddr: 0xfffffe0008774000+0x96b9c -> 0xfffffe000880ab9c [1, 509108] funcaddr: 0xfffffe0008774000+0x7c4b4 -> 0xfffffe00087f04b4 [1, 8496472] funcaddr: 0xfffffe0008774000+0x81a558 -> 0xfffffe0008f8e558 [1, 53004] funcaddr: 0xfffffe0008774000+0xcf0c -> 0xfffffe0008780f0c ['atos', '-o', 'Kernel_Debug_Kit_26_build_25A353.dmg_extracted/KDK.pkg/Payload/System/Library/Kernels/kernel.release.t8103.dSYM/Contents/Resources/DWARF/kernel.release.t8103', '-arch', 'arm64e', '-l', '0xfffffe002c630000', '0xfffffe000880c010', '0xfffffe000880ab9c', '0xfffffe00087f04b4', '0xfffffe0008f8e558', '0xfffffe0008780f0c'] 0xfffffe000880c010 0xfffffe000880ab9c 0xfffffe00087f04b4 0xfffffe0008f8e558 0xfffffe0008780f0c UUID: 8502a040-9cf9-35f5-b8a2-84b0e48d379e is that of the kernel as referenced in the panic log Kernel UUID: 8502A040-9CF9-35F5-B8A2-84B0E48D379E and given in the binaryImages lookup table located at index 1: "binaryImages": [ [ "fbe15ad4-ea36-3c07-81be-460a8240c1d4", 18446741874887413328, "T" ], [ "8502a040-9cf9-35f5-b8a2-84b0e48d379e", 18446741874828328960, "T" ], Function addresses are computed as follows: faddr=binaryImages[loadaddr]+kernelFrames[offset]. Given the diagnostics above [1, 622608] funcaddr: 0xfffffe0008774000+0x98010 -> 0xfffffe000880c010, where 0xfffffe0008774000 is the load address of the kernel 18446741874828328960, and 0x98010 is the offset from the load address 622608, atos(1) fails to perform symbolication. Your second suggestion to use the value of the offset+ the load address of 0 doesn't succeed either. Your clarifying how function addresses given in kernel stack frames are to be computed so they result in a successful symbolication would be greatly appreciated.
Topic: App & System Services SubTopic: Core OS Tags:
Oct ’25
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
SO, the question here is how/why an ACL was set on the file but the xattr was not created. The ACL was set on the file with chmod(1): % chmod +a 'developer allow write,append,delete' /Volumes/myfs/nf.txt In any case, the real key point is here: Next, the ACL extracted from the source file sec object is applied to the brand-new file sec object created via CFFileSecuritySetAccessControlList(newFileSec,tmpACL): At the point this succeeded, the file should have acquired a "com.apple.system.Security" attribute, since that's where your file system should be storing the ACL. Why didn't it? Actually, it did. The output I supplied was meant to demonstrate it, 0xaf54acd10 being the new file sec object: <FileSecurity 0xaf54acd10> [0xaf6a71980] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_MODE = 33060, FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = !#acl 1 user:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append } I guess I should've made it explicitly clear that CFFileSecuritySetAccessControlList(newFileSec,tmpACL) did succeed. That matters because the reason copyExtendedAttribute did this first: Calls getxattr(dstFile, 'com.apple.system.Security', NULL, 0, 0, XATTR_NOFOLLOW) to obtain the size of the attribute. -1 is returned. ...is because IF an xattr had been returned there, as it should have been, copyExtendedAttribute would have then skipped this step: Calls getxattr(srcFile, 'com.apple.system.Security', buffer, 512, 0, XATTR_NOFOLLOW) to obtain the ACL EA. -1 is returned with errno==1 EPERM. ...and simply continued normally without any error. Your statement about copyExtendedAttribute skipping these getxattr(2) calls isn't accurate. I'll expand on that later. Both getxattr(2) calls fail with EPERM because com.apple.system.Security is a protected EA and the TextEdit process is running with regular privileges and it doesn't have FILESEC_ACCESS_ENTITLEMENT, getxattr(2): if (xattr_protected(attrname) && (error = xattr_entitlement_check(attrname, ctx, false)) != 0) { goto out; } I dynamically analysed these functions, URLReplaceObject,CFURLCopySecurityEAs,TransferExtendedAttributes,copyExtendedAttribute, for APFS to see which code paths are taken on a successful run. The file resides on APFS: % df -h apfs.txt Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/disk4s5 367Gi 105Gi 241Gi 31% 1.0M 2.5G 0% /System/Volumes/Data And uses the following POSIX and ACL permissions: % ls -le apfs.txt -r--r--r--+ 1 developer staff 6 Sep 28 16:20 apfs.txt 0: user:developer allow write,delete,append The ACL permissions on apfs.txt were created with chmod(1): % chmod +a 'developer allow write,append.delete' apfs.txt I found that the code behaves as I had observed with my filesystem, i.e.: A new file sec object is created. The ACL is obtained from the source file's file sec object. The ACL is stored in the new file sec object. The new file sec object is stored in a dictionary CFDictionaryAddValue(dict,NSURLFileSecurityKey,newFileSec) for further use. TransferExtendedAttributes(alloc,srcURL,dstURL,&error) gets called for APFS as well. Because the file had no EAs created by TextEdit yet, later on com.apple.TextEncoding gets added for this file by TextEdit, but that has no bearing on the ACL situation. TransferExtendedAttributes calls listxattr(2) which returns zero. TransferExtendedAttributes returns two values to the caller: The status code returned is 1, probably a Boolean value and The CFError parameter is set to NULL on success. This is where the behaviour differs between my filesystem and APFS. My filesystem returns 0 in the status code and a non-null CFError. On TransferExtendedAttributes returning success for APFS, the actual call that writes the new file sec object out to disk is: CFURLSetResourcePropertiesForKeys(dstURL,dict,&error): dstURL: file:///Users/developer/Library/Containers/com.apple.TextEdit/Data/tmp/TemporaryItems/NSIRD_TextEdit_EfokSZ/apfs.txt dict: { NSURLCreationDateKey = "2026-09-28 13:03:50 +0000"; NSURLFileSecurityKey = "<FileSecurity 0xc289c1f50> [0xc28aec680] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_M ODE = 33060, FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = !#acl 1\nuser:ED27D762-80A1-496B-BD81-4AE8CE54CE82: developer:501:allow:write,delete,append\n}"; } And this is it. To see if this would result in the same code path being taken for my filesystem, I modified the status code and the CFError object returned by TransferExtendedAttributes dynamically to 1 and NULL respectively while debugging with Ghidra and lldb, and had CFURLSetResourcePropertiesForKeys(dstURL,dict,&error) called with the file contents being successfully written out to disk. To the best of my knowledge, this brings us back to my earlier assumption, which has now been confirmed by these findings, that TextEdit and the like aren't permitted to query com.apple.system.Security. The only way to have TextEdit interoperate with ACLs is to have them stored as file metadata instead of as an extended attribute.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
To better understand the situation I resorted to dynamic analisys of the following CoreServicesInternal functions using Ghidra, a software reverse engineering tool: URLReplaceObject CFURLCopySecurityEAs TransferExtendedAttributes copyExtendedAttribute The following test case was considered: Source file mode: 0444 Source file ACL: user id 501 allow write,append,delete The temporary file, created by TextEdit, i.e. the destination file mode: 0644 srcUrlMode != dstUrlMode Execution path CFURLCopySecurityEAs(srcURL,dstURL,&local_8d0) This function is called based on xpc_runtime_is_app_sandboxed() returning true and isSrcURLDirectory==isDstURLDirectory. Internally, CFURLCopySecurityEAs(): Opens both srcURL and dstURL Retrieves a list of EAs from srcURL via flistxattr(2): % sudo xattr /Volumes/myfs/nf.txt com.apple.quarantine com.apple.TextEncoding com.apple.lastuseddate#PS com.apple.system.Security Iterates over the list of EAs returned Copies any EAs that match the com.apple.security. pattern, e.g.: % kextstat 2>/dev/null | awk '$6~/com.apple.security./{print $6}' com.apple.security.AppleImage4 com.apple.security.sandbox com.apple.security.quarantine com.apple.security.BootPolicy The value returned is 1. Next, the file security object associated with the source and destination URLs is obtained via CFURLCopyResourcePropertyForKey: CFURLCopyResourcePropertyForKey(srcURL,NSURLFileSecurityKey,&srcFileSec,&error); CFURLCopyResourcePropertyForKey(dstURL,NSURLFileSecurityKey,&dstFileSec,&error); Next, a new file security object is created via CFFileSecurityCreate: <FileSecurity 0xaf54acd10> [0xaf6a71980] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_MODE = (null), FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = (null)} Next, the file mode 33060==o100444 is extracted from srcFileSec via CFFileSecurityGetMode(srcFileSec,&srcUrlMode): <FileSecurity 0xa3f5dee60> [0xa3fbf0a80] {FILESEC_OWNER = 501, FILESEC_GROUP = 20, FILESEC_MODE = 33060, FILESEC_UUID = 0000000 0-0000-0000-0000-000000000000, FILESEC_GRPUUID = 00000000-0000-0000-0000-000000000000, FILESEC_ACL = !#acl 1 user:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append } Next, the GID 20 (staff) of the owner of the source and the destination files is extracted from srcFileSec and dstFileSec respectively via CFFileSecurityGetGroup: CFFileSecurityGetGroup(srcFileSec,(long)&srcgid) CFFileSecurityGetGroup(dstFileSec,&dstgid) Next, the mode of the source file is applied to the brand new file sec object via CFFileSecuritySetMode(newFileSec,srcUrlMode): <FileSecurity 0xaf54acd10> [0xaf6a71980] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_MODE = 33060, FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = (null)} Next, the ACL is extracted from the source file sec object via CFFileSecurityCopyAccessControlList(srcFileSec,&tmpACL): <FileSecurity 0xa3f5dee60> [0xa3fbf0a80] {FILESEC_OWNER = 501, FILESEC_GROUP = 20, FILESEC_MODE = 33060, FILESEC_UUID = 0000000 0-0000-0000-0000-000000000000, FILESEC_GRPUUID = 00000000-0000-0000-0000-000000000000, FILESEC_ACL = !#acl 1 user:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append } (lldb) expr char* $tmpACL = (char*)acl_to_text(*(void**)0x16d35a138, (long*)0) (lldb) p (char*)$tmpACL (lldb) "!#acl 1\nuser:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append\n" Next, the ACL extracted from the source file sec object is applied to the brand new file sec object created via CFFileSecuritySetAccessControlList(newFileSec,tmpACL): <FileSecurity 0xaf54acd10> [0xaf6a71980] {FILESEC_OWNER = (null), FILESEC_GROUP = (null), FILESEC_MODE = 33060, FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = !#acl 1 user:ED27D762-80A1-496B-BD81-4AE8CE54CE82:developer:501:allow:write,delete,append } Next, the destination file mode o100644 is extracted from the destination file sec object via CFFileSecurityGetMode(dstFileSec,&dstUrlMode): <FileSecurity 0xa3f5def70> [0xa3fbf0200] {FILESEC_OWNER = 501, FILESEC_GROUP = 20, FILESEC_MODE = 33188 (o100644), FILESEC_UUID = (null), FILESEC_GRPUUID = (null), FILESEC_ACL = (null)} Next, the brand new file sec object is added to a dictionary: CFDictionaryAddValue(dict,NSURLFileSecurityKey,newFileSec) Next, TransferExtendedAttributes(alloc,srcURL,dstURL,&error) is called. Internally, TransferExtendedAttributes: Obtains a list of EAs via listxattr(2) Calls xattr_preserve_for_intent for each of the EAs iteratively passing XATTR_OPERATION_INTENT_SAVE. Calls copyExtendedAttribute to copy the ACL EA from the source to the destination file. xattr_preserve_for_intent considers the following EAs worth saving: com.apple.lastuseddate#PS com.apple.system.Security The EAs are saved by calling copyExtendedAttribute. com.apple.lastuseddate#PS is saved successfully. com.apple.system.Security fails to be saved. Internally, copyExtendedAttribute: Calls getxattr(dstFile, 'com.apple.system.Security', NULL, 0, 0, XATTR_NOFOLLOW) to obtain the size of the attribute. -1 is returned. Calls getxattr(srcFile, 'com.apple.system.Security', buffer, 512, 0, XATTR_NOFOLLOW) to obtain the ACL EA. -1 is returned with errno==1 EPERM. TransferExtendedAttributes returns 0 to the caller and the errno in the error parameter. URLReplaceObject proceeds down the error path and ends up setting a CFError to: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “nf.txt.sb-4b9a8883-7jQtqz” in the folder “myfs”." UserInfo={NSURL=file:///Volumes/myfs/nf.txt.sb-4b9a8883-7jQtqz, NSFilePath=/Volumes/myfs/nf.txt.sb-4b9a8883-7jQtqz, NS UnderlyingError=0xaf76952c0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}} URLReplaceObject returns 0x201==513 to the caller. If you have any further guidance to offer, I would greatly appreciate it.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
Are you seeing an ACL get on the source? Or the ACL set on the destination? I was able to map this syscall returning ENOATTR: 2 => getxattr('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', value:0, size:0, position:0, options:1) TextEdit-3772 3 <= getxattr('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', value:0, size:0, position:0, options:1) -> -1 (93) TextEdit-3772 ;ENOATTR To this vnop_getxattr call in my kext, with args->a_size=0 returned: vnop_getxattr: TextEdit-3772 -> getxattr_rpc('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', fffffe2fbb998180, uio_resid(EAvalue):762) vnop_getxattr: TextEdit-3772 <- getxattr_rpc('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', fffffe2fbb998180, uio_resid(EAvalue):762) -> 93 ;ENOATTR vnop_getxattr: TextEdit-3772 '/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt' 'com.apple.system.Security' *args->a_size=0 vnop_getxattr: TextEdit-3772 '/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt' 'com.apple.system.Security' <- 93 ;ENOATTR The next syscall succeeds in returning the com.apple.system.Security EA, with the EA size of 512 bytes returned. This differs from the size returned by my kext being 68 bytes. See below: 3 => getxattr('/Volumes/myfs/f.txt', 'com.apple.system.Security', value:16de4d390, size:512, position:0, options:1) TextEdit-3772 0 <= getxattr('/Volumes/myfs/f.txt', 'com.apple.system.Security', value:16de4d390, size:512, position:0, options:1) -> 512 (0) TextEdit-3772 vnop_getxattr: TextEdit-3772 -> getxattr_rpc('/Volumes/myfs/f.txt', 'com.apple.system.Security', fffffecff9e3ba80, uio_resid(EAvalue):512) vnop_getxattr: TextEdit-3772 <- getxattr_rpc('/Volumes/myfs/f.txt', 'com.apple.system.Security', fffffecff9e3ba80, uio_resid(EAvalue):488) -> 68 vnop_getxattr: TextEdit-3772 '/Volumes/myfs/f.txt' 'com.apple.system.Security' *args->a_size=68 vnop_getxattr: TextEdit-3772 '/Volumes/myfs/f.txt' 'com.apple.system.Security' <- 0 ; KERN_SUCCESS This syscall never calls into the vnop_setxattr in my kext: 0 => setxattr('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', value:16de4d390, size:512, position:0, options:1) TextEdit-3772 0 <= setxattr('/Volumes/myfs/f.txt.sb-94c724e7-egshwF/f.txt', 'com.apple.system.Security', value:16de4d390, size:512, position:0, options:1) -> -1 (1) TextEdit-3772 ;EPERM EPERM possibly being returned from setxattr via the following execution path: if (xattr_protected(sactx->attrname) && (error = xattr_entitlement_check(sactx->attrname, ctx, true)) != 0) { goto out; }
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
[quote='904084022, DTS Engineer, /thread/843772?answerId=904084022#904084022'] What are the permissions of the parent directory, and do they allow object creation? [/quote] The parent directory that houses the test file is the root of the filesystem which uses the most liberal POSIX permissions and has no ACL permissions set: % ls -led /Volumes/myfs/ drwxrwxrwx 8 root wheel 15 Sep 4 16:53 /Volumes/myfs/ I've been able to identify the point of failure in TextEdit using dtrace(1) via the syscall provider. When replacing the original file, /Volumes/myfs/f.txt, with the temporary copy, TextEdit queries the presence of the com.apple.system.Security EA in the copy, /Volumes/myfs/f.txt.sb-1f18ed33-4luthh/f.txt. It gets ENOATTR returned, because the attribute isn't there. Next, the original file is queried to get the size of the EA. Finally, an attempt is made to create the security EA for the copy. It's this call that returns EPERM. Extended attributes are queried and set using the following execution path, NSDocument saveToURL -> NSDocument writeSafelyToURL -> NSFileManager replaceItemAtURL -> CoreServicesInternal _URLReplaceObject -> CoreServicesInternal TransferExtendedAttributes -> libsystem_kernel.dylib getxattr or libsystem_kernel.dylibsetxattr` See the file attached, dtrace-textedit-xattr.txt, for the diagnostics emitted by dtrace(1). dtrace-textedit-xattr.txt I guess, the only options available to me are, apps like TextEdit, Pages and possibly some other apps like that as well shouldn't be used to modify files when ACLs are stored as the system security EA, or the ACL implementation in my filesystem kext should be redone using the ATTR_CMN_EXTENDED_SECURITY VFS file metadata attribute.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
Is it possible that YOU ended up calling "getxattr" yourself as part of the permission check? No, I do not call getxattr explicitly. Both setxattr and getxattr are called by the system and my filesystem just stores and retrieves the ACL EA from the server. I've built a test app with the following entitlements enabled: % codesign -d --entitlements - --xml SandboxedFileEditor.app | plutil -p - Executable=SandboxedFileEditor.app/Contents/MacOS/SandboxedFileEditor { "com.apple.developer.applesignin" => { "com.apple.security.app-sandbox" => true "com.apple.security.files.bookmarks.app-scope" => true "com.apple.security.files.user-selected.read-write" => true } "com.apple.security.app-sandbox" => true "com.apple.security.files.user-selected.read-write" => true "com.apple.security.get-task-allow" => true } I was able to successfully modify and save the contents of the test file residing on my custom filesystem: % cat /Volumes/myfs/f.txt line1 line2 With the following POSIX and ACL permissions set: % ls -le /Volumes/myfs/f.txt -r--r--r--@ 1 user staff 6 Sep 3 17:55 /Volumes/myfs/f.txt 0: user:user allow write,delete,append And the ACL being stored as a com.apple.system.Security EA: % sudo xattr /Volumes/myfs/f.txt com.apple.TextEncoding com.apple.quarantine com.apple.system.Security I still don't understand how to debug TextEdit and Pages not being permitted to save the modified file contents. In the system log, I've found the following error message: 2026-08-14 16:50:41.313445+0300 0x2718a Error 0x62db7 442 0 Finder: (Foundation) [com.apple.foundation.filecoordination:claims] 73600188-34F7-4F6C-A697-34A74474169C grantAccessClaim reply is an error: Error Domain=NSCocoaErrorDomain Code=513 "The document “f1.txt” could not be autosaved. You don’t have permission." UserInfo={NSUnderlyingError=0x83891b900 {Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “f1.txt” in the folder “." UserInfo={NSURL=file:///Volumes/myfs/f1.txt, NSUnderlyingError=0x83891b8d0 {Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “f1.txt.sb-23e6090e-qqHT2x” in the folder “." UserInfo={NSURL=file:///Volumes/myfs/f1.txt.sb-23e6090e-qqHT2x, NSFilePath=/Volumes/myfs/f1.txt.sb-23e6090e-qqHT2x, NSUnderlyingError=0x83891bc00 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}}}}, NSLocalizedDescription=The document “f1.txt” could not be autosaved. You don’t have permission., NSURL=file:///Volumes/myfs/f1.txt, NSLocalizedFailure Do you have any further suggestions to make in this respect?
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Sep ’26
Reply to Sanboxed Apps Reading Extended Security Information (ACL)
Thanks for your response. When evaluating KAUTH_FILESEC_XATTR via getxattr(), xattr_entitlement_check() succeeds if the calling process is that of the superuser, or if it carries the FILESEC_ACCESS_ENTITLEMENT entitlement. TextEdit does not cary the FILESEC_ACCESS_ENTITLEMENT: % codesign -d --entitlements - --xml /System/Applications/TextEdit.app | plutil -p - Executable=/System/Applications/TextEdit.app/Contents/MacOS/TextEdit { "com.apple.application-identifier" => "com.apple.TextEdit" "com.apple.developer.ubiquity-container-identifiers" => [ 0 => "com.apple.TextEdit" ] "com.apple.private.hid.client.event-dispatch.internal" => true "com.apple.security.app-sandbox" => true "com.apple.security.files.user-selected.executable" => true "com.apple.security.files.user-selected.read-write" => true "com.apple.security.print" => true } This explains the behaviour I'm observing, i.e. running TextEdit as a regular user fails to write file changes and returning EPERM, and running TextEdit as a superuser succeeds in writing the file contents. xattr(1) behaves the same way: % ls -le /Volumes/myfs/f.txt -r--r--r--@ 1 user staff 0 Aug 27 18:39 /Volumes/myfs/f.txt 0: user:user allow write,delete,append % xattr /Volumes/myfs/f.txt com.apple.TextEncoding xattr: [Errno 1] Operation not permitted: '/Volumes/myfs/f.txt' % sudo xattr /Volumes/myfs/f.txt com.apple.TextEncoding com.apple.system.Security What I need clarified is this, if my custom filesystem kext handles ACL data as the KAUTH_FILESEC_XATTR, which it does in the current implementation, then sandboxed apps won't be able to save modified file contents, because they fail the xattr_entitlement_check(). Is my only option is this case to handle ACL data through ATTR_CMN_EXTENDED_SECURITY? Which the PoC I wrote confirmed as working.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Aug ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
For the sake of completeness, I thought I'd mention that I have been able to debug this by using the Quarantine Queue coupled with Delayed Deallocation, a technique employed by address sanitisers. It works by retaining objects to be released and placing them onto a quarantine queue of a finite size, overwriting the entire object's memory with a canary value like 0xdb or similar to cause a system fault when accessed. When the quarantine queue fills up, it's drained below the set threshold by releasing the memory back to the system.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Aug ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
I've done some further research into a possibility of building and linking a custom kext against the kasan kernel. I've put the following CFLAGS into my Xcode project based on the MakeInc.def Apple's own kasan kernels are built with. OTHER_CFLAGS = -DKASAN_TBI=1 -DKASAN_SCALE=4 -fsanitize=kernel-hwaddress -fsanitize-ignorelist=$(SRCROOT)/../../../../xnu/san/memory/kasan-denylist-arm64 -mllvm -hwasan-recover=0 -mllvm -hwasan-mapping-offset=0xf000000000000000 -mllvm -hwasan-instrument-atomics=1 -mllvm -hwasan-instrument-stack=1 -mllvm -hwasan-generate-tags-with-calls=1 -mllvm -hwasan-instrument-with-calls=0 -mllvm -hwasan-use-short-granules=0 -mllvm -hwasan-memory-access-callback-prefix="__asan_" Xcode emitted the following error error: unsupported option '-fsanitize=kernel-hwaddress' for target 'arm64-apple-macos26.0'. According to this post, https://discourse.llvm.org/t/hw-address-sanitizer-on-arm64-apple-darwin/76153/5, on the llvm support forum, the Xcode toolchain doesn't support kernel address sanitisation: Yes, they have it in XNU. But if HWASan is enabled, it’s not compilable. By default, the clang from XCode is used, so it’s Apple’s own. Tried with apple’s fork of llvm-project which is actively maintained but it’s also unable to compile for target arm64-apple-darwin. If want to experiment, here is a useful all-in-one script that does building, all you need to change is the KERNEL_CONFIG to KASAN. Do I understand it correctly that any further attempts to build and link my custom kext with address sanitisation enabled are futile and should be abandoned?
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Jul ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
I've managed to boot a KASAN kernel on Apple Silicon running macOS 15.7.4 build 24G517 by following the instructions, https://kernelshaman.blogspot.com/2021/02/building-xnu-for-macos-112-intel-apple.html, you referneced in a reply above. I was also able to build my custom KEXT with these CFLAGS='-fsanitize=address -mllvm -asan-globals=0' as done by the XNU unit tests: ifeq ($(BUILD_ASAN),1) BUILD_SANITIZERS = 1 # compile XNU with asan # TODO: enable globals instrumentation and write a proper ignorelist for problematic global vars XNU_CFLAGS_EXTRA += -fsanitize=address -mllvm -asan-globals=0 # make mocks code aware of sanitizers runtime being linked SANITIZERS_FLAGS += -D__BUILDING_WITH_ASAN__=1 endif # BUILD_ASAN Attempting to build a boot collection with my asan-instrumented KEXT results in a bunch of undefined asan symbols errors emitted. How do I build my KEXT against a KASAN kernel so I can debug the UAFs it has? Thanks.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Jul ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
You're right about the kernel clearing the thread state I set up via those debug registers. One place this happens in is the machine_thread_set_state function where the monitor debug system control register gets cleared and the debug watchpoint control registers and the debug watchpoint value registers get reset. Thanks for looking into this. I won't be filing a bug for this. I've instrumented the memory allocation call sites with diagnostics to output the allocation size and the data type to match that against the info on the memory that got corrupted by a UAF given in the kernel panic log. I'm now endeavouring to manually track down the code paths that lead to the UAFs.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Jul ’26
Reply to Pinpointing dandling pointers in 3rd party KEXTs
I've been exploring the possibility of debugging UAFs by setting up hardware watchpoints via the CPU debug registers on Apple Silicon. In particular, I've set up the MDE and KDE bits in the MDSCR_EL1 register, enabled the relevant bits in the DBGWCR register to track both load and store operations, and set up the DBGWVR register to track memory addresses of interest. I have a consistent UAF reproducer, and my custom KEXT I've instrumented via inline assembly doesn't seem to detect memory IO after it's been free'd. So I was wondering, if it's down to me not being aware of some other CPU registers that may need setting up or is it because the system enforces some restrictions on 3rd party KEXTs attempting to manipulate those debug registers. Your clarifying this would be greatly appreciated.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Jun ’26
Reply to Symbolicating kernel backtraces on Apple Silicon
Based on the symbolicated backtraces, there are no references to where the memory is being used after being freed, or where it's being freed. There's just this reference to where it's being allocated: ['atos', '-o', '/Users/user/panic/Kernel_Debug_Kit_15.6_build_24G84.dmg_extracted/KDK.pkg/Payload/System/Library/Kernels/kernel.release.t8103.dSYM/Contents/Resources/DWARF/kernel.release.t8103', '-arch', 'arm64e', '-textExecAddress', '0xfffffe0025c50000', '0xfffffe0025cae7c0', '0xfffffe0026558328', '0xfffffe002655e730', '0xfffffe0025d30f08', '0xfffffe0025cc21c0', '0xfffffe002620afe4', '0xfffffe00243f717c', '0xfffffe00243f7110', '0xfffffe00243df12c', '0xfffffe00243df610', '0xfffffe0024408bc0', '0xfffffe0025ee60cc', '0xfffffe0025ecdf08', '0xfffffe0025eceee4', '0xfffffe002635e9b8', '0xfffffe0025e0fff0', '0xfffffe0025c57b88'] panic_trap_to_debugger (in kernel.release.t8103) (debug.c:1400) kalloc_type_distribute_budget (in kernel.release.t8103) (kalloc.c:1343) kmem_init (in kernel.release.t8103) (vm_kern.c:4584) zone_early_scramble_rr (in kernel.release.t8103) (zalloc.c:3220) kalloc_ext (in kernel.release.t8103) (kalloc.c:2520) mprotect (in kernel.release.t8103) (kern_mman.c:1311) 0xfffffe00243f717c 0xfffffe00243f7110 0xfffffe00243df12c 0xfffffe00243df610 0xfffffe0024408bc0 vn_pathconf (in kernel.release.t8103) (vfs_vnops.c:1875) openat_dprotected_internal (in kernel.release.t8103) (vfs_syscalls.c:5127) mknod (in kernel.release.t8103) (vfs_syscalls.c:5469) sendsig (in kernel.release.t8103) (unix_signal.c:626) sleh_synchronous (in kernel.release.t8103) (sleh.c:1267) fleh_synchronous (in kernel.release.t8103) + 24 The unsymbolicated memory addresses come from my KEXT where memory is allocated via OSMalloc(). The object being allocated ends up being stored in an RB tree. I'd like to take you up on your offer to take a look at the full panic log for me if I may. Please find the full panic log attached to this ticket https://feedbackassistant.apple.com/feedback/21231833.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Dec ’25
Reply to Symbolicating kernel backtraces on Apple Silicon
Hello Kevin, I put together a Python script to do a full symbolication based on your instructions. I'm having trouble computing addresses of functions given in the actual stack frames. First off, the symbolication of the frame of the thread that panicked does succeed as the function addresses come precomputed by the system: % ./symbolicate.py TEXT_EXEC 0xfffffe00072d4000 TEXT 0xfffffe0007004000 ktext_exec_base 0xfffffe002c900000 load_address: 0xfffffe002c630000 panicked_thread_faddrs: ['0xfffffe002c95d93c', '0xfffffe002cacd124', '0xfffffe002cacb31c', '0xfffffe002c903b88', '0xfffffe002c95dc08', '0xfffffe002d264898', '0xfffffe002d2700f8', '0xfffffe002caccf80', '0xfffffe002cacb490', '0xfffffe002c903b88', '0xfffffe002cb7f820', '0xfffffe002cba8538', '0xfffffe002cb8fd00', '0xfffffe002cb905c0', '0xfffffe002d05eb5c', '0xfffffe002cacb3a4', '0xfffffe002c903b88', '0x19d82a2b0'] ['atos', '-o', 'Kernel_Debug_Kit_26_build_25A353.dmg_extracted/KDK.pkg/Payload/System/Library/Kernels/kernel.release.t8103.dSYM/Contents/Resources/DWARF/kernel.release.t8103', '-arch', 'arm64e', '-l', '0xfffffe002c630000', '0xfffffe002c95d93c', '0xfffffe002cacd124', '0xfffffe002cacb31c', '0xfffffe002c903b88', '0xfffffe002c95dc08', '0xfffffe002d264898', '0xfffffe002d2700f8', '0xfffffe002caccf80', '0xfffffe002cacb490', '0xfffffe002c903b88', '0xfffffe002cb7f820', '0xfffffe002cba8538', '0xfffffe002cb8fd00', '0xfffffe002cb905c0', '0xfffffe002d05eb5c', '0xfffffe002cacb3a4', '0xfffffe002c903b88', '0x19d82a2b0'] handle_debugger_trap (in kernel.release.t8103) (debug.c:1863) handle_uncategorized (in kernel.release.t8103) (sleh.c:1818) sleh_synchronous (in kernel.release.t8103) (sleh.c:1698) fleh_synchronous (in kernel.release.t8103) + 24 DebuggerTrapWithState (in kernel.release.t8103) (debug.c:830) Assert (in kernel.release.t8103) (debug.c:841) sleh_synchronous_sp1 (in kernel.release.t8103) (sleh.c:1191) handle_kernel_abort (in kernel.release.t8103) (sleh.c:3960) sleh_synchronous (in kernel.release.t8103) (sleh.c:1698) fleh_synchronous (in kernel.release.t8103) + 24 vn_create (in kernel.release.t8103) (vfs_subr.c:8079) vn_open_auth (in kernel.release.t8103) (vfs_vnops.c:483) open1 (in kernel.release.t8103) (vfs_syscalls.c:0) open_extended (in kernel.release.t8103) (vfs_syscalls.c:5273) unix_syscall (in kernel.release.t8103) (systemcalls.c:181) sleh_synchronous (in kernel.release.t8103) (sleh.c:1484) fleh_synchronous (in kernel.release.t8103) + 24 0x19d82a2b0 Here's the output generated for the very first kernel frame that fails to symbolicate: kernelFrames: tid:110 IOServiceTerminateThread UUID: 8502a040-9cf9-35f5-b8a2-84b0e48d379e [1, 622608] funcaddr: 0xfffffe0008774000+0x98010 -> 0xfffffe000880c010 [1, 617372] funcaddr: 0xfffffe0008774000+0x96b9c -> 0xfffffe000880ab9c [1, 509108] funcaddr: 0xfffffe0008774000+0x7c4b4 -> 0xfffffe00087f04b4 [1, 8496472] funcaddr: 0xfffffe0008774000+0x81a558 -> 0xfffffe0008f8e558 [1, 53004] funcaddr: 0xfffffe0008774000+0xcf0c -> 0xfffffe0008780f0c ['atos', '-o', 'Kernel_Debug_Kit_26_build_25A353.dmg_extracted/KDK.pkg/Payload/System/Library/Kernels/kernel.release.t8103.dSYM/Contents/Resources/DWARF/kernel.release.t8103', '-arch', 'arm64e', '-l', '0xfffffe002c630000', '0xfffffe000880c010', '0xfffffe000880ab9c', '0xfffffe00087f04b4', '0xfffffe0008f8e558', '0xfffffe0008780f0c'] 0xfffffe000880c010 0xfffffe000880ab9c 0xfffffe00087f04b4 0xfffffe0008f8e558 0xfffffe0008780f0c UUID: 8502a040-9cf9-35f5-b8a2-84b0e48d379e is that of the kernel as referenced in the panic log Kernel UUID: 8502A040-9CF9-35F5-B8A2-84B0E48D379E and given in the binaryImages lookup table located at index 1: "binaryImages": [ [ "fbe15ad4-ea36-3c07-81be-460a8240c1d4", 18446741874887413328, "T" ], [ "8502a040-9cf9-35f5-b8a2-84b0e48d379e", 18446741874828328960, "T" ], Function addresses are computed as follows: faddr=binaryImages[loadaddr]+kernelFrames[offset]. Given the diagnostics above [1, 622608] funcaddr: 0xfffffe0008774000+0x98010 -> 0xfffffe000880c010, where 0xfffffe0008774000 is the load address of the kernel 18446741874828328960, and 0x98010 is the offset from the load address 622608, atos(1) fails to perform symbolication. Your second suggestion to use the value of the offset+ the load address of 0 doesn't succeed either. Your clarifying how function addresses given in kernel stack frames are to be computed so they result in a successful symbolication would be greatly appreciated.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Oct ’25