Closed RokLenarcic closed 7 years ago
Great, someone with macOS! As the README states, I have only had a chance to compile-test on macOS.
Which value did you pass for the preferred_protocols
parameter to Card::connect
? (The examples use PROTOCOL_ANY
).
Do you happen to know which protocol your card reader is using? If it's a USB card reader, it's probably T0, whose value is 0x0001
- which is what you get if you remove the 0x7fff
part.
Maybe I have an incorrect integer type somewhere, or maybe macOS has some different behavior, or a bug. But I'll wait for you answers first.
Sorry, Card::connect
-> Context::connect
.
I ran the connect example. I checked out your repository and did the following:
pub type DWORD = u32;
pub type LONG = i32;
pub type ULONG = u32;
Boom!
Readers: ["Gemalto PC Twin Reader"]
Status: STATUS_PRESENT | STATUS_POWERED | STATUS_SPECIFIC
Protocol: T0
RAPDU: [106, 130]
ATR: [59, 59, 148, 0, 0, 100, 14, 62, 2, 240, 49, 128, 14, 144, 0]
Vendor IFD version: [0, 0, 0, 2]
Vendor name: Gemalto
Works. Looks like macOS, despite being 64-bit operating system, uses a 32-bit library here?
Yes, it's most likely that these are defined differently for macOS. Looking at pcsclite's code, it looks like the types you wrote are correct: https://github.com/LudovicRousseau/PCSC/blob/88a53f3ed21aa645e7c65661162f832a053fb9de/src/PCSC/wintypes.h#L46
But I know that macOS no longer uses pcsclite, but a custom implementation. So it would be nice if you could maybe look at the headers and see how they typedef DWORD
& friends these days. I'm not sure where C header files are located in macOS, but if you find it, look at the files PCSC/wintypes.h
, PCSC/winscard.h
.
BTW, it would be great if you can check whether the monitor
example program works; this has traditionally been the buggiest on macOS.
Thanks!
Monitor example just prints "Adding "Gemalto PC Twin Reader" and exits instantly.
/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* MUSCLE SmartCard Development ( http://www.linuxnet.com )
*
* Copyright (C) 1999-2003
* David Corcoran <corcoran@linuxnet.com>
* Ludovic Rousseau <ludovic.rousseau@free.fr>
*
* $Id: winscard.h 123 2010-03-27 10:50:42Z ludovic.rousseau@gmail.com $
*/
/**
* @file
* @brief This handles smartcard reader communications.
*/
#ifndef __winscard_h__
#define __winscard_h__
#include <PCSC/pcsclite.h>
#include <stdint.h>
//#include "pcscexport.h"
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef PCSC_API
#define PCSC_API
#endif
PCSC_API int32_t SCardEstablishContext(uint32_t dwScope,
const void *pvReserved1, const void *pvReserved2, LPSCARDCONTEXT phContext);
PCSC_API int32_t SCardReleaseContext(SCARDCONTEXT hContext);
PCSC_API int32_t SCardIsValidContext(SCARDCONTEXT hContext);
PCSC_API int32_t SCardSetTimeout(SCARDCONTEXT hContext, uint32_t dwTimeout);
PCSC_API int32_t SCardConnect(SCARDCONTEXT hContext,
const char *szReader,
uint32_t dwShareMode,
uint32_t dwPreferredProtocols,
LPSCARDHANDLE phCard, uint32_t *pdwActiveProtocol);
PCSC_API int32_t SCardReconnect(SCARDHANDLE hCard,
uint32_t dwShareMode,
uint32_t dwPreferredProtocols,
uint32_t dwInitialization, uint32_t *pdwActiveProtocol);
PCSC_API int32_t SCardDisconnect(SCARDHANDLE hCard, uint32_t dwDisposition);
PCSC_API int32_t SCardBeginTransaction(SCARDHANDLE hCard);
PCSC_API int32_t SCardEndTransaction(SCARDHANDLE hCard, uint32_t dwDisposition);
PCSC_API int32_t SCardCancelTransaction(SCARDHANDLE hCard);
PCSC_API int32_t SCardStatus(SCARDHANDLE hCard,
char *mszReaderNames, uint32_t *pcchReaderLen,
uint32_t *pdwState,
uint32_t *pdwProtocol,
unsigned char *pbAtr, uint32_t *pcbAtrLen);
PCSC_API int32_t SCardGetStatusChange(SCARDCONTEXT hContext,
uint32_t dwTimeout,
LPSCARD_READERSTATE_A rgReaderStates, uint32_t cReaders);
PCSC_API int32_t SCardControl(SCARDHANDLE hCard,
const void *pbSendBuffer, uint32_t cbSendLength,
void *pbRecvBuffer, uint32_t *pcbRecvLength);
PCSC_API int32_t SCardControl132(SCARDHANDLE hCard, uint32_t dwControlCode,
const void *pbSendBuffer, uint32_t cbSendLength,
void *pbRecvBuffer, uint32_t cbRecvLength, uint32_t *lpBytesReturned);
PCSC_API int32_t SCardTransmit(SCARDHANDLE hCard,
LPCSCARD_IO_REQUEST pioSendPci,
const unsigned char *pbSendBuffer, uint32_t cbSendLength,
LPSCARD_IO_REQUEST pioRecvPci,
unsigned char *pbRecvBuffer, uint32_t *pcbRecvLength);
PCSC_API int32_t SCardListReaderGroups(SCARDCONTEXT hContext,
char *mszGroups, uint32_t *pcchGroups);
PCSC_API int32_t SCardListReaders(SCARDCONTEXT hContext,
const char *mszGroups,
char *mszReaders, uint32_t *pcchReaders);
PCSC_API int32_t SCardCancel(SCARDCONTEXT hContext);
PCSC_API int32_t SCardGetAttrib(SCARDHANDLE hCard, uint32_t dwAttrId,
uint8_t *pbAttr, uint32_t *pcbAttrLen);
PCSC_API int32_t SCardSetAttrib(SCARDHANDLE hCard, uint32_t dwAttrId,
const uint8_t *pbAttr, uint32_t cbAttrLen);
void SCardUnload(void);
#ifdef __cplusplus
}
#endif
/*
To support the newer version of SCardControl, we define it
as follows. The old version number was 1.1.2, the new call
appears in 1.3.2 of pcsc-lite (or perhaps earlier).
*/
#if !defined(USE_SCARD_CONTROL_112)
#define SCardControl SCardControl132
#endif /* USE_SCARD_CONTROL_112 */
#endif
/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* MUSCLE SmartCard Development ( http://www.linuxnet.com )
*
* Copyright (C) 1999
* David Corcoran <corcoran@linuxnet.com>
*
* $Id: wintypes.h 123 2010-03-27 10:50:42Z ludovic.rousseau@gmail.com $
*/
/**
* @file
* @brief This keeps a list of Windows(R) types.
*/
#ifndef __wintypes_h__
#define __wintypes_h__
#ifdef __cplusplus
extern "C"
{
#endif
#if !defined(WIN32)
#include <stdint.h>
#ifndef BYTE
typedef uint8_t BYTE;
#endif
typedef uint8_t UCHAR;
typedef uint8_t *PUCHAR;
typedef uint16_t USHORT;
#ifndef __COREFOUNDATION_CFPLUGINCOM__
typedef uint32_t ULONG;
typedef void *LPVOID;
typedef int16_t BOOL;
#endif
typedef uint32_t *PULONG;
typedef const void *LPCVOID;
typedef uint32_t DWORD;
typedef uint32_t *PDWORD;
typedef uint16_t WORD;
typedef int32_t LONG;
typedef int32_t RESPONSECODE;
typedef const char *LPCSTR;
typedef const BYTE *LPCBYTE;
typedef BYTE *LPBYTE;
typedef DWORD *LPDWORD;
typedef char *LPSTR;
/* these types are deprecated but still used by old drivers and applications
* You should use LPSTR instead */
typedef char *LPTSTR ;
typedef const char *LPCTSTR ;
typedef char *LPCWSTR
#ifdef __GNUC__
/* __attribute__ is a GCC only extension */
__attribute__ ((deprecated))
#endif
;
#else
#include <windows.h>
#endif
#ifdef __cplusplus
}
#endif
#endif
Monitor example crashes on
try_pcsc!(ffi::SCardGetStatusChange(
self.handle,
timeout_ms,
readers.as_mut_ptr() as *mut ffi::SCARD_READERSTATE,
readers.len() as DWORD,
));
But there's no error.
I pushed a fix for the types in pcsc-sys. I'll do a release soon.
I guessed that monitor
wouldn't work, because of https://ludovicrousseau.blogspot.co.il/2015/12/os-x-el-capitan-missing-feature.html. But it definitely should return some error/panic, and not crash silently. Can you run the program under gdb
(or maybe it's lldb
now), and see if you can get a backtrace? That would be something like that:
$ git pull # Pull the latest fix
$ cargo test --all
$ gdb ./target/debug/examples/monitor
(gdb) r
<here it should crash>
(gdb) bt full
and paste the output.
First think I noticed is that it complains about c_long being unused import. The second thing is that it doesn't complain about c_ulong being unused. There are two of those still in there. I am not sure if that is ok or not. I also noticed that the cancel example exits normally after 5 seconds instead of exiting with a timeout.
LLDB:
(lldb) target create "./target/debug/examples/monitor"
Current executable set to './target/debug/examples/monitor' (x86_64).
(lldb) r
Process 30110 launched: './target/debug/examples/monitor' (x86_64)
Adding "Gemalto PC Twin Reader"
Process 30110 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x00007fff97ebdb52 libsystem_c.dylib`strlen + 18
libsystem_c.dylib`strlen:
-> 0x7fff97ebdb52 <+18>: pcmpeqb (%rdi), %xmm0
0x7fff97ebdb56 <+22>: pmovmskb %xmm0, %esi
0x7fff97ebdb5a <+26>: andq $0xf, %rcx
0x7fff97ebdb5e <+30>: orq $-0x1, %rax
I am not sure if that is ok or not.
It's probably not OK! Just to make sure that these should be u32
, can you paste the definition of the SCARD_IO_REQUEST
struct? It should be in one of the headers under PCSC/
.
I will fix the unused import warnings after that.
EXC_BAD_ACCESS
Hmm, that's strange. What I'm thinking is that the definition of SCARD_READERSTATE
in pcsc-sys is incorrect for macOS. So, can you paste the definition of this struct as well? And also the definition of MAX_ATR_SIZE
(if there is one), which is the size of the rgbAtr
array in that struct.
Thanks again :)
typedef struct
{
const char *szReader;
void *pvUserData;
uint32_t dwCurrentState;
uint32_t dwEventState;
uint32_t cbAtr;
unsigned char rgbAtr[MAX_ATR_SIZE];
}
SCARD_READERSTATE_A;
typedef SCARD_READERSTATE_A SCARD_READERSTATE, *PSCARD_READERSTATE_A,
*LPSCARD_READERSTATE_A;
/** Protocol Control Information (PCI) */
typedef struct _SCARD_IO_REQUEST
{
uint32_t dwProtocol; /**< Protocol identifier */
uint32_t cbPciLength; /**< Protocol Control Inf Length */
}
SCARD_IO_REQUEST, *PSCARD_IO_REQUEST, *LPSCARD_IO_REQUEST;
typedef const SCARD_IO_REQUEST *LPCSCARD_IO_REQUEST;
extern SCARD_IO_REQUEST g_rgSCardT0Pci, g_rgSCardT1Pci,
g_rgSCardRawPci;
#define MAX_ATR_SIZE 33 /**< Maximum ATR size */
I've pushed fixes for the unused imports and the definition of SCARD_IO_REQUEST
.
The definition of SCARD_READERSTATE
is correct, however, so that's not the cause.
Maybe the fix to SCARD_IO_REQUEST
also fixes the monitor crash? Do you mind trying that one again? If it still doesn't work, please show the output of bt full
in the lldb
prompt.
Well now I get:
Adding "Gemalto PC Twin Reader"
Segmentation fault: 11
And here's the debug (I used bt all, bt full doesn't seem to be a valid command).
(lldb) r
Process 30944 launched: './target/debug/examples/monitor' (x86_64)
Adding "Gemalto PC Twin Reader"
Process 30944 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x00007fff97ebdb52 libsystem_c.dylib`strlen + 18
libsystem_c.dylib`strlen:
-> 0x7fff97ebdb52 <+18>: pcmpeqb (%rdi), %xmm0
0x7fff97ebdb56 <+22>: pmovmskb %xmm0, %esi
0x7fff97ebdb5a <+26>: andq $0xf, %rcx
0x7fff97ebdb5e <+30>: orq $-0x1, %rax
(lldb) bt all
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
* frame #0: 0x00007fff97ebdb52 libsystem_c.dylib`strlen + 18
frame #1: 0x00007fff97f1892b libsystem_c.dylib`strdup + 18
frame #2: 0x00007fff980d1b40 libxpc.dylib`xpc_string_create + 11
frame #3: 0x00007fff980d1b12 libxpc.dylib`xpc_dictionary_set_string + 24
frame #4: 0x00007fff876ecd3f PCSC`__SCardGetStatusChange_block_invoke + 86
frame #5: 0x00007fff876ec854 PCSC`transact + 484
frame #6: 0x00007fff876ecc71 PCSC`SCardGetStatusChange + 428
frame #7: 0x0000000100004245 monitor`pcsc::{{impl}}::get_status_change<core::option::Option<std::time::duration::Duration>>(self=0x00007fff5fbfeb48, timeout=Option<std::time::duration::Duration> @ 0x00007fff5fbfe978, readers=&mut [pcsc::ReaderState] @ 0x00007fff5fbfe990) at lib.rs:804
frame #8: 0x0000000100005b6b monitor`monitor::main at monitor.rs:42
frame #9: 0x00000001000146fb monitor`panic_unwind::__rust_maybe_catch_panic at lib.rs:98 [opt]
frame #10: 0x0000000100013dd7 monitor`std::rt::lang_start [inlined] std::panicking::try<(),fn()> at panicking.rs:433 [opt]
frame #11: 0x0000000100013da8 monitor`std::rt::lang_start [inlined] std::panic::catch_unwind<fn(),()> at panic.rs:361 [opt]
frame #12: 0x0000000100013da8 monitor`std::rt::lang_start at rt.rs:57 [opt]
frame #13: 0x0000000100005f7a monitor`main + 42
frame #14: 0x00007fff97e87235 libdyld.dylib`start + 1
thread #2
frame #0: 0x00007fff97fb644e libsystem_kernel.dylib`__workq_kernreturn + 10
frame #1: 0x00007fff980a0695 libsystem_pthread.dylib`_pthread_wqthread + 1426
frame #2: 0x00007fff980a00f1 libsystem_pthread.dylib`start_wqthread + 13
thread #3
frame #0: 0x00007fff97fb644e libsystem_kernel.dylib`__workq_kernreturn + 10
frame #1: 0x00007fff980a0502 libsystem_pthread.dylib`_pthread_wqthread + 1023
frame #2: 0x00007fff980a00f1 libsystem_pthread.dylib`start_wqthread + 13
Could it be a problem with pointer (reference) width? Or the wrong ABI (extern "system" vs extern "C")?
I think the problem might be that SCARD_IO_REQUEST
and SCARD_READERSTATE
should be packed on macOS. I had missed that on reading the pcsclite.h
header on my machine.
Would you please try applying this patch, and see if it fixes the problem?
diff --git a/pcsc-sys/src/lib.rs b/pcsc-sys/src/lib.rs
index 686cab4..429ca41 100644
--- a/pcsc-sys/src/lib.rs
+++ b/pcsc-sys/src/lib.rs
@@ -146,7 +146,8 @@ pub const MAX_ATR_SIZE: usize = 33;
pub const MAX_BUFFER_SIZE: usize = 264;
pub const MAX_BUFFER_SIZE_EXTENDED: usize = 4 + 3 + (1 << 16) + 3 + 2;
-#[repr(C)]
+#[cfg_attr(not(target_os = "macos"), repr(C))]
+#[cfg_attr(target_os = "macos", repr(C, packed))]
pub struct SCARD_IO_REQUEST {
pub dwProtocol: DWORD,
pub cbPciLength: DWORD,
@@ -157,7 +158,8 @@ pub const ATR_BUFFER_SIZE: usize = MAX_ATR_SIZE;
#[cfg(windows)]
pub const ATR_BUFFER_SIZE: usize = 36;
-#[repr(C)]
+#[cfg_attr(not(target_os = "macos"), repr(C))]
+#[cfg_attr(target_os = "macos", repr(C, packed))]
pub struct SCARD_READERSTATE {
pub szReader: *const c_char,
pub pvUserData: *mut c_void,
It works. Interestingly if I slide the card in and out quickly I sometimes get a status update where the CHANGED flag is missing.
"Gemalto PC Twin Reader" STATE_CHANGED | STATE_PRESENT
"Gemalto PC Twin Reader" STATE_CHANGED | STATE_EMPTY
"Gemalto PC Twin Reader" STATE_EMPTY
"Gemalto PC Twin Reader" STATE_CHANGED | STATE_PRESENT
Well the example that doesn't work correctly remaining is the cancel one. Reports a normal exit instead of timeout.
Blocking call exited normally
Great! I pushed this fix.
The article I linked to before (https://ludovicrousseau.blogspot.co.il/2015/12/os-x-el-capitan-missing-feature.html) says that \\?PnP?\Notification
is not supported on macOS. I wonder if this is fixed nowadays? Can the monitor detect reader insertions & removals (the reader itself, not a card within a reader).
About the missing CHANGED, this seems reasonable the state hasn't changed from the previous one (STATE_EMPTY
). I guess spurious wakeups are possible.
Well the example that doesn't work correctly remaining is the cancel one.
It actually doesn't work for me either now! Let me see what's going on (I wish it was possible to have tests for this... Maybe with qemu).
I tried monitor again and I got this:
Adding "Gemalto PC Twin Reader"
"Gemalto PC Twin Reader" STATE_CHANGED | STATE_PRESENT
Removing "\\\\?PnP?\\Notification"
I have the reader connected with the card in.
Here's 3 different scenarios, from here on out, that make it panic:
Second:
Third:
Removing "Gemalto PC Twin Reader"
Removing "Gemalto PC Twin Reader"
thread 'main' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:678
stack backtrace:
0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
1: std::panicking::default_hook::{{closure}}
2: std::panicking::default_hook
3: std::panicking::rust_panic_with_hook
4: std::panicking::begin_panic
5: std::panicking::begin_panic_fmt
6: rust_begin_unwind
7: core::panicking::panic_fmt
8: core::slice::slice_index_order_fail
9: <core::ops::Range<usize> as core::slice::SliceIndex<[T]>>::index
10: <core::ops::RangeFrom<usize> as core::slice::SliceIndex<[T]>>::index
11: core::slice::<impl core::ops::Index<I> for [T]>::index
12: <collections::vec::Vec<T> as core::ops::Index<core::ops::RangeFrom<usize>>>::index
13: monitor::main
14: __rust_maybe_catch_panic
15: std::rt::lang_start
16: main
I was able to reproduce the cancel problem in C as well. Something must have changed. I sent a question to the pcsclite mailing list, maybe they can tell what's going on: http://lists.alioth.debian.org/pipermail/pcsclite-muscle/2017-June/000921.html
As for the panics, they do not reproduce on linux/pcsclite. I suspect the problem is that the PNP_NOTIFICATION
reader state, which the program assumes is always at position zero, returns is_dead -> true
, then is removed from the vector, and then this panics: reader_states[1..]
.
To verify this, can you try this diff, and paste the output of one of the scenarios you mentioned?
diff --git a/pcsc/examples/monitor.rs b/pcsc/examples/monitor.rs
index ee5bd81..3fba4df 100644
--- a/pcsc/examples/monitor.rs
+++ b/pcsc/examples/monitor.rs
@@ -27,7 +27,7 @@ fn main() {
// Add new readers.
let names = ctx.list_readers(&mut readers_buf).expect("failed to list readers");
for name in names {
- if !reader_states[1..].iter().any(|rs| rs.name() == name) {
+ if !reader_states.iter().any(|rs| rs.name() == name) {
println!("Adding {:?}", name);
reader_states.push(ReaderState::new(name, STATE_UNAWARE));
}
@@ -43,7 +43,7 @@ fn main() {
// Print current state.
println!();
- for rs in &reader_states[1..] {
+ for rs in &reader_states {
println!("{:?} {:?}", rs.name(), rs.event_state());
}
}
Also, if possible, run it in debug mode; this makes stacktraces a bit more readable.
Thanks!
Your patch makes it work.
OK, I fixed the panics.
I'll close this issue now, and open a separate issue to track the cancel problem. I'll release 0.1.1 later today.
Thanks for reporting & debugging everything here. Let me know if you run into any other issues.
I'm not sure about this bug but I tried to run some of your examples and I've got the following error when reading the protocol of the card reader:
OS: macOS
Could it be possible that you are grabbing too many bits when determining protocols? That 0x7fff start seems awfully suspicious.