Discussion:
Multiple threads and SCardGetStatusChange
Martin Paljak
2009-11-30 15:42:02 UTC
Permalink
Hi.

What is the status of multithreaded access to pcsc-lite? Is it supposed to work?

SCardGetStatusChange(timeout=INFINITE) with latest pcsc-lite fails as it holds the mutex for ... infinite time :) The Windows test shows that a SCardGetStatusChange(timeout=0) is supposed to return even when a "infinitely" blocking call is going on.

I attach a simple test program written with python/pyscard and the output from Winows:

Context established!
PCSC Readers: ['OMNIKEY CardMan 1021 0']
----- Current reader and card states are: -------
OMNIKEY CardMan 1021 0 0x3B 0x6E 0x00 0xFF 0x45 0x73 0x74 0x45 0x49 0x44 0x20 0x76 0x65 0x72 0x20 0x31 0x2E 0x30
Card present in reader
State changed
Launching a new thread
Sleeping a little ...
----- Please insert or remove a card ------------ <--- don't really insert anything
Getting status without a timeout
Got statuses: <--- a non-blocking access in another thread returns right away.
OMNIKEY CardMan 1021 0 0x3B 0x6E 0x00 0xFF 0x45 0x73 0x74 0x45 0x49 0x44 0x20 0x76 0x65 0x72 0x20 0x31 0x2E 0x30
Card present in reader
State changed
----- New reader and card states are: -----------
OMNIKEY CardMan 1021 0
Reader empty
State changed
Released context.




This makes my re-written blocking C_WaitForSlotEvent() implementation in OpenSC unusable on non-windows platforms :(

I don't know enough about pcsc-lite internals to fix the problem.
Ludovic Rousseau
2009-12-01 07:57:39 UTC
Permalink
Post by Martin Paljak
Hi.
Hello,
Post by Martin Paljak
What is the status of multithreaded access to pcsc-lite? Is it supposed to work?
Yes. But each thread needs to have its own context. You shall call
SCardEstablishContext() in the newly created thread and you program
should work (not yet tested with your sample).

I already tried to detect the problem and return
SCARD_E_INVALID_HANDLE but that is not easy.

I note to add this "limitation" to the "Known differences with
Microsoft Windows WinSCard implementation:"

Bye
--
Dr. Ludovic Rousseau
Sébastien Lorquet
2009-12-01 08:44:47 UTC
Permalink
hi

here we have a multi card application that uses one thread and one
SCardEstablishContext per card, and the load is prettily balanced among the
multiple cards. We can also add and remove cards at runtime. We tested the
system with up to 20 cards, it worked without a glitch. The overall
transaction time is really reduced with more cards. (we have a command
queuing daemon).

The only problem is when we remove usb readers when the application is
running (using polled libusb, not hal)
But that's ok.

(this post is not a bug report but a success report :-) )

Regards,
Sebastien.
Post by Ludovic Rousseau
Post by Martin Paljak
Hi.
Hello,
Post by Martin Paljak
What is the status of multithreaded access to pcsc-lite? Is it supposed
to work?
Yes. But each thread needs to have its own context. You shall call
SCardEstablishContext() in the newly created thread and you program
should work (not yet tested with your sample).
I already tried to detect the problem and return
SCARD_E_INVALID_HANDLE but that is not easy.
I note to add this "limitation" to the "Known differences with
Microsoft Windows WinSCard implementation:"
Bye
--
Dr. Ludovic Rousseau
_______________________________________________
Muscle mailing list
http://lists.drizzle.com/mailman/listinfo/muscle
Ludovic Rousseau
2009-12-01 08:51:01 UTC
Permalink
Post by Sébastien Lorquet
hi
here we have a multi card application that uses one thread and one
SCardEstablishContext per card, and the load is prettily balanced among the
multiple cards. We can also add and remove cards at runtime. We tested the
system with up to 20 cards, it worked without a glitch. The overall
transaction time is really reduced with more cards. (we have a command
queuing daemon).
Thanks for your return.
Post by Sébastien Lorquet
The only problem is when we remove usb readers when the application is
running (using polled libusb, not hal)
But that's ok.
How could you describe the problem?

Bye
--
Dr. Ludovic Rousseau
Sébastien Lorquet
2009-12-01 09:00:27 UTC
Permalink
I'm working on other software atm.
But I 'll have a set of unit tests to run with this application in the
coming days.
I'll tell you the syslog messages I find.

I don't remember if the pcsc daemon was crashing or stopped working, or our
application crashed.
Readers are all ccid Teo by Xiring and/or SDI SCR3310 (and occasionnaly
gemplus gray square ones).

Seb
Post by Ludovic Rousseau
Post by Sébastien Lorquet
hi
here we have a multi card application that uses one thread and one
SCardEstablishContext per card, and the load is prettily balanced among
the
Post by Sébastien Lorquet
multiple cards. We can also add and remove cards at runtime. We tested
the
Post by Sébastien Lorquet
system with up to 20 cards, it worked without a glitch. The overall
transaction time is really reduced with more cards. (we have a command
queuing daemon).
Thanks for your return.
Post by Sébastien Lorquet
The only problem is when we remove usb readers when the application is
running (using polled libusb, not hal)
But that's ok.
How could you describe the problem?
Bye
--
Dr. Ludovic Rousseau
_______________________________________________
Muscle mailing list
http://lists.drizzle.com/mailman/listinfo/muscle
Martin Paljak
2009-12-01 09:44:59 UTC
Permalink
Post by Ludovic Rousseau
Post by Martin Paljak
What is the status of multithreaded access to pcsc-lite? Is it supposed to work?
Yes. But each thread needs to have its own context. You shall call
SCardEstablishContext() in the newly created thread and you program
should work (not yet tested with your sample).
OK, I noticed the comment in SCardEstablichContext documentation of pcsc-lite: Each thread of an application shall use its own SCARDCONTEXT.

Apparently other calls work (in sequential mode?) so that it has not been noticed before. In theory is it important to call SCardEstablishContext for every thread and every API call or in real life it only affects cases like SCardGetStatusChange with infinite timeout?

It is not straightforward to fix the situation in OpenSC(PKCS#11) either, as there is no way to know in PKCS#11 if a new thread has been started than to keep a list of existing thread id-s and check that one of the id-s maches for every pcsc-lite call and manage a list of context handles as well..

Although not necessary for SCardGetStatusChange (which only requires a context handle), if the requirement would be true for all API calls, then every new thread would also have to make the full connection establishment to be able to talk to the card?
Post by Ludovic Rousseau
I already tried to detect the problem and return
SCARD_E_INVALID_HANDLE but that is not easy.
I note to add this "limitation" to the "Known differences with
Microsoft Windows WinSCard implementation:"
Shall this remain like this by design or will it be fixed?
--
Martin Paljak
http://martin.paljak.pri.ee
+372.515.6495
Sébastien Lorquet
2009-12-01 09:55:20 UTC
Permalink
Post by Martin Paljak
In theory is it important to call SCardEstablishContext for every thread
and every API call or in real life it only affects cases like
Post by Martin Paljak
SCardGetStatusChange with infinite timeout?
Also in practice.

If I understand correctly, SCardEstablishContext() creates a socket
connection using UNIX domain sockets to pcscd. The PCSC API is blocking (you
do not receive card responses in a callback) so imagine the socket is also
blocking, this explains why you need a SCARDCONTEXT for each thread.

Seb
Sébastien Lorquet
2009-12-01 09:59:35 UTC
Permalink
Sorry for the spamming:

I may have said something not true: Is the connection opened by
ScardEstablishContext, or by SCardConnect? I would say the first. This means
we really need both a SCARDHANDLE and a SCARDCONTEXT in each thread.

Seb
Ludovic Rousseau
2009-12-01 10:18:41 UTC
Permalink
Post by Sébastien Lorquet
I may have said something not true: Is the connection opened by
ScardEstablishContext, or by SCardConnect? I would say the first. This means
we really need both a SCARDHANDLE and a SCARDCONTEXT in each thread.
SCardConnect() is done after ScardEstablishContext() so yes, you need
to have a card handle different for each thread.

Bye
--
Dr. Ludovic Rousseau
Ludovic Rousseau
2009-12-01 10:23:51 UTC
Permalink
Post by Martin Paljak
Post by Ludovic Rousseau
Post by Martin Paljak
What is the status of multithreaded access to pcsc-lite? Is it supposed to work?
Yes. But each thread needs to have its own context. You shall call
SCardEstablishContext() in the newly created thread and you program
should work (not yet tested with your sample).
OK, I noticed the comment in SCardEstablichContext documentation of pcsc-lite: Each thread of an application shall use its own SCARDCONTEXT.
Apparently other calls work (in sequential mode?) so that it has not been noticed before. In theory is it important to call SCardEstablishContext for every thread and every API call or in real life it only affects cases like SCardGetStatusChange with infinite timeout?
All PC/SC calls of a same context will use the same mutex. So if the
calls are sequential they will just work. But if one call blocks
(SCardGetStatusChange with infinite timeout) then the other calls Will
also block.
Post by Martin Paljak
It is not straightforward to fix the situation in OpenSC(PKCS#11) either, as there is no way to know in PKCS#11 if a new thread has been started than to keep a list of existing thread id-s and check that one of the id-s maches for every pcsc-lite call and manage a list of context handles as well..
Is PKCS#11 supposed to support threads?
Post by Martin Paljak
Although not necessary for SCardGetStatusChange (which only requires a context handle), if the requirement would be true for all API calls, then every new thread would also have to make the full connection establishment to be able to talk to the card?
I don't think it is a good idea to have two threads talking to the
same card at the same time. But pcsc-lite will/should handle that.

Bye
--
Dr. Ludovic Rousseau
Waldemar Dick
2009-12-01 10:56:27 UTC
Permalink
Hi,
Post by Ludovic Rousseau
Post by Martin Paljak
What is the status of multithreaded access to pcsc-lite? Is it supposed to work?
Yes. But each thread needs to have its own context. You shall call
SCardEstablishContext() in the newly created thread and you program
should work (not yet tested with your sample).
following this thread, if I understood corretly, each thread needs its
own SCARDCONTEXT.
But to cancel a blocking SCardGetStatusChange(timeout=INFINITE), I have
to call SCardCancel() from an other thread.
So, which SCARDCONTEXT handle do I pass as an argument to SCardCancel()?
Either does SCardCancel() cancel all waiting calls across all
established contexts or I don't understand how this could work?

Greetings,

Waldemar Dick
Ludovic Rousseau
2009-12-01 11:09:32 UTC
Permalink
following this thread, if I understood corretly, each thread needs its own
SCARDCONTEXT.
But to cancel a blocking SCardGetStatusChange(timeout=INFINITE), I have to
call SCardCancel() from an other thread.
So, which SCARDCONTEXT handle do I pass as an argument to SCardCancel()?
Either does SCardCancel() cancel all waiting calls across all established
contexts or I don't understand how this could work?
SCardCancel() is the only exception to the rule.
You have to call SCardCancel() with the same context used in
SCardGetStatusChange().

Bye
--
Dr. Ludovic Rousseau
Martin Paljak
2009-12-01 13:07:02 UTC
Permalink
Post by Ludovic Rousseau
following this thread, if I understood corretly, each thread needs its own
SCARDCONTEXT.
But to cancel a blocking SCardGetStatusChange(timeout=INFINITE), I have to
call SCardCancel() from an other thread.
So, which SCARDCONTEXT handle do I pass as an argument to SCardCancel()?
Either does SCardCancel() cancel all waiting calls across all established
contexts or I don't understand how this could work?
SCardCancel() is the only exception to the rule.
You have to call SCardCancel() with the same context used in
SCardGetStatusChange().
Are you sure you did not want to say that SCardCancel MUST NOT be called with the same context as SCardGetStatusChange was called?


Otherwise it would mean that SCardGetSlotEvent/SCardCancel is not really usable for PKCS#11 (or anything). From PKCS#11 spec:
"""
If C_WaitForSlotEvent is called with the CKF_DONT_BLOCK flag clear in the flags argument, then the call behaves as above, except that it will block. That is, if no slot’s event flag is set at the time of the call, C_WaitForSlotEvent will wait until some slot’s event flag becomes set. If a thread of an application has a C_WaitForSlotEvent call blocking when another thread of that application calls C_Finalize , the C_WaitForSlotEvent call returns with the value CKR_CRYPTOKI_NOT_INITIALIZED.
"""
On Windows, this can be accomplished by calling SCardCancel() in C_Finalize so that C_WaitForSlotEvent() can return before uninitializing the PKCS#11 module.
--
Martin Paljak
http://martin.paljak.pri.ee
+372.515.6495
Ludovic Rousseau
2009-12-01 13:44:44 UTC
Permalink
Post by Martin Paljak
Post by Ludovic Rousseau
following this thread, if I understood corretly, each thread needs its own
SCARDCONTEXT.
But to cancel a blocking SCardGetStatusChange(timeout=INFINITE), I have to
call SCardCancel() from an other thread.
So, which SCARDCONTEXT handle do I pass as an argument to SCardCancel()?
Either does SCardCancel() cancel all waiting calls across all established
contexts or I don't understand how this could work?
SCardCancel() is the only exception to the rule.
You have to call SCardCancel() with the same context used in
SCardGetStatusChange().
Are you sure you did not want to say that SCardCancel MUST NOT be called with the same context as SCardGetStatusChange was called?
No. I was correct.
The hContext argument of SCardCancel() is used to find the
corresponding SCardGetStatusChange().
Post by Martin Paljak
"""
If C_WaitForSlotEvent is called with the CKF_DONT_BLOCK flag clear in the flags argument, then the call behaves as above, except that it will block. That is, if no slot’s event flag is set at the time of the call, C_WaitForSlotEvent will wait until some slot’s event flag becomes set. If a thread of an application has a C_WaitForSlotEvent call blocking when another thread of that application calls C_Finalize , the C_WaitForSlotEvent call returns with the value CKR_CRYPTOKI_NOT_INITIALIZED.
"""
On Windows, this can be accomplished by calling SCardCancel() in C_Finalize so that C_WaitForSlotEvent() can return before uninitializing the PKCS#11 module.
Why can't you do the same on Linux?

You just have to store the hContext used by C_WaitForSlotEvent() and
call SCardCancel() on it in C_Finalize(). You can even have more than
1 C_WaitForSlotEvent() running and would then need to cancel them all.

Bye
--
Dr. Ludovic Rousseau
Martin Paljak
2009-12-01 13:00:31 UTC
Permalink
Post by Ludovic Rousseau
Post by Martin Paljak
It is not straightforward to fix the situation in OpenSC(PKCS#11) either, as there is no way to know in PKCS#11 if a new thread has been started than to keep a list of existing thread id-s and check that one of the id-s maches for every pcsc-lite call and manage a list of context handles as well..
Is PKCS#11 supposed to support threads?
Yes. PKCS#11 supports threaded access to PKCS#11 module as well as (in theory) the module to launch private threads. The major exception that deserves a special comment in the spec is C_WaitForSlotEvent which is exactly the thing that need a blocking SCardGetStatusChange and a SCardCancel (cancel is called when C_Finalize is called). From PKCS#11 v2.20, page 149:
"""Although the parameters supplied to C_Initialize can in general allow for safe multi- threaded access to a Cryptoki library, C_WaitForSlotEvent is exceptional in that the behavior of Cryptoki is undefined if multiple threads of a single application make simultaneous calls to C_WaitForSlotEvent."""
Post by Ludovic Rousseau
Post by Martin Paljak
Although not necessary for SCardGetStatusChange (which only requires a context handle), if the requirement would be true for all API calls, then every new thread would also have to make the full connection establishment to be able to talk to the card?
I don't think it is a good idea to have two threads talking to the
same card at the same time. But pcsc-lite will/should handle that.
*Card access* is serialized, reader/card state access is not.
--
Martin Paljak
http://martin.paljak.pri.ee
+372.515.6495
Loading...