Вопрос

I am writing a Python script that calls functions from Security.framework on OS X through ctypes.

I was able to make a CFDictionaryRef and CFStringRef in Python, and store the string in the dictionary, but now that I'm adding Security.framework query objects (kSecClass, kSecMatchLimit, kSecReturnAttributes, …) I get the following crash:

objc[28201]: Method cache corrupted. This may be a message to an invalid object, or a memory error somewhere else.
objc[28201]: receiver 0x7fff724fcdf8, SEL 0x7fff8d814fe5, isa 0x7fff724f5f90, cache 0x7fff724f5fa0, buckets 0x7fff8842ffe3, mask 0x5, occupied 0x0, wrap bucket 0x7fff8842ffe3
objc[28201]: receiver 0 bytes, buckets 0 bytes
objc[28201]: selector 'hash'
objc[28201]: isa '(null)'
objc[28201]: Method cache corrupted.
zsh: illegal hardware instruction  python kc.py

I've tried calling CFDictionaryCreate() with ctypes arrays containing the objects, and also calling CFMutableDictionaryCreate() then CFDictionarySetValue. See below.

import ctypes
import ctypes.util
from ctypes import CFUNCTYPE

security = ctypes.CDLL(ctypes.util.find_library('Security'))
foundation = ctypes.CDLL(ctypes.util.find_library('Foundation'))

CFDictionaryRef = ctypes.c_void_p
CFMutableDictionaryRef = ctypes.c_void_p
CFTypeRef = ctypes.c_void_p
CFIndex = ctypes.c_long

CFDictionaryCreate = CFUNCTYPE(CFDictionaryRef, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, CFIndex, ctypes.c_void_p, ctypes.c_void_p)(('CFDictionaryCreate', foundation))
CFDictionaryCreateMutable = CFUNCTYPE(CFMutableDictionaryRef, CFIndex, ctypes.c_void_p, ctypes.c_void_p)(('CFDictionaryCreateMutable', foundation))
CFDictionarySetValue = CFUNCTYPE(CFMutableDictionaryRef, ctypes.c_void_p, ctypes.c_void_p)(('CFDictionarySetValue', foundation))

def createCFDictionary(items):
    """
    Create a CFDictionaryRef from items = [(CFTypeRef, CFTypeRef), ...]
    """
    # Create C arrays for the keys and values (which are cast as CFTypeRefs).
    keys_array = (CFTypeRef * len(items))(*[ctypes.cast(k, CFTypeRef) for k, v in items])
    values_array = (CFTypeRef * len(items))(*[ctypes.cast(v, CFTypeRef) for k, v in items])

    return CFDictionaryCreate(None,
                              keys_array, values_array, len(items),
                              foundation.kCFTypeDictionaryKeyCallBacks, foundation.kCFTypeDictionaryValueCallBacks)

# 1. Create a CFDictionary with the items in it (crashes).
query = createCFDictionary([(security.kSecClass, security.kSecClassCertificate)])


# 2a. Create a CFMutableDictionary.
query = CFDictionaryCreateMutable(0, foundation.kCFTypeDictionaryKeyCallBacks, foundation.kCFTypeDictionaryValueCallBacks)

# 2b. Add items to it (crashes).
CFDictionarySetValue(query, security.kSecClass, security.kSecClassCertificate)

I am happy to use PyObjC instead of ctypes if that's an easier solution; I am less familiar with it, however, and haven't been able to import Security.framework.

OS X 10.9.2, Python 2.7.5 (included with OS X).

Это было полезно?

Решение

I'm looking into case #2, because it seems simpler.

Your CFDictionarySetValue is missing the dict argument. After fixing that, it appears that security.kSecClass is actually returning a function reference, which is bad. I think you need to:

ctypes.c_void_p.in_dll(foundation, 'kSecClass')

Which results in the correct value being passed to the function (as seen in lldb).

Somehow the kCFTypeDictionaryKeyCallBacks structs still don't work with that mechanism, probably because the symbols actually point to a struct, not a pointer-to-struct. I can't figure out any way to get ctypes to give me back the actual address the symbol represents (which is what you want to pass to CFDictionaryCreateMutable) rather than the value at that symbol (which is 0, since that's the value of the first member of that struct).

You could construct the callbacks yourself, which should be easier as you can define the struct type in ctypes and then just grab the needed functions.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top