Help with PAM and ctypes

Lenard Lindstrom len-l at telus.net
Fri Jun 15 09:50:21 CEST 2007


Chris AtLee wrote:
> On Jun 11, 6:01 pm, Lenard Lindstrom <l... at telus.net> wrote:
> [snip snip snip]
>>> if __name__ == "__main__":
>>>     import getpass, os, sys
>>>     @conv_func
>>>     def my_conv(nMessages, messages, pResponse, appData):
>>>         # Create an array of nMessages response objects
>>>         # Does r get GC'ed after we're all done?
>>>         r = (pam_response * nMessages)()
>> The memory allocated to r is garbage collected immediately after my_conv
>> returns. You need to allocate it explicitly using C's calloc or such.
>> This assumes pam_start will free the memory for you.
>>
>> addr = calloc(sizeof(pam_response), nMessages)
>>
>> addr is the memory address as a Python integer.
>>
>>>         pResponse.contents = cast(r, POINTER(pam_response))
>> pResponse.contents changes the actual value of pResponse, a value on the
>> stack. You want to change the value of the pointer pResponse points to:
>>
>> pResponse[0] = cast(addr, POINTER(pam_response))
>>
>> The cast creates a POINTER(pam_reponse) instance with value addr.
> 
> Ahhh, thank you!  I never understood how ctypes' pointer.contents
> related
> to C pointers.  So, the following are equivalent?
> 
> int  v = 42;        v = 42
> int *p = 0;         p = cast(0, POINTER(c_int))
> p = &v;             p.contents = v
> *p = 123;           p[0] = 123

Actually

int v = 42;     v = c_int(42)

for p.contents = v to work. 42 is a Python integer. c_int(42) refers to 
a mutable C integer in memory initialized to 42. Also, the preferred way 
to create a NULL pointer in ctypes is to call the pointer type without 
an argument:

int *p = 0;     p = POINTER(c_int)()


> 
> Using "pResponse[0] = cast(...)" got me part of the way to fixing my
> problem.  PAM started either crashing or saying authentication had
> failed.
> The crash was in free(), and some digging around revealed that PAM
> tries to
> free the response sent by the application, which makes sense.
> 
> My initial attempt to fix this involved wrapping strdup to allocate a
> new
> copy of a string to send back to PAM.  Here's how I wrapped it:
> 
> strdup = libc.strdup
> strdup.argstypes = [c_char_p]
> strdup.restype = c_char_p
> 
> This still crashed in free().  I took a look at some of the ctypes
> regression tests and something made me try this:
> 
> strdup = libc.strdup
> strdup.argstypes = [c_char_p]
> strdup.restype = POINTER(c_char) # NOT c_char_p !!!!
> 
> This works like a charm.  Not sure why though...Does ctypes do
> something
> special with c_char_p return types?  It seems as if Python was freeing
> the
> result of strdup() when the string was GC'ed, and then PAM would fail
> when
> trying to free the same memory.
> 

c_char_p implicitly converts to/from a Python string. So strdup with a 
c_char_p restype returns a copy of the returned C string as a Python 
string instead of a pointer to the memory allocated by strdup. And I 
don't know how a Python string is cast to a c_char_p.

--
Lenard Lindstrom
<len-l at telus.net>



More information about the Python-list mailing list