endpoint security framework in multithread applications

Hello, I am going to use endpoint security framework in my application. Unfortunately, I have not found any clear cut explanation about how to use endpoint security in multithread applications.

  • Can we say the handler block (es_handler_block_t), which is the parameter of es_new_client(), is running in separate thread?
  • Does es_new_client() create new thread?
  • Should I synchronize a handler block and the code which calls es_delete_client()? Should I protect the handler block by mutex?

Thank you in advance.

Accepted Reply

I am going to use endpoint security framework in my application.

To be clear, ES clients are not applications. You have two choices:

  • An ES system extension

  • A launchd daemon that calls ES


Regarding your first two questions, when you call es_new_client the system calls the supplied block from a custom serialised context that it manages.

Regarding your third question, many ES clients never call es_delete_client because they run indefinitely. However, if you do call it then, as the doc comments make clear, you must call it from the same thread that called es_new_client. Most folks do both of these from the main thread.

IMPORTANT Because of these constraints, es_delete_client inherently races with event delivery.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

I am going to use endpoint security framework in my application.

To be clear, ES clients are not applications. You have two choices:

  • An ES system extension

  • A launchd daemon that calls ES


Regarding your first two questions, when you call es_new_client the system calls the supplied block from a custom serialised context that it manages.

Regarding your third question, many ES clients never call es_delete_client because they run indefinitely. However, if you do call it then, as the doc comments make clear, you must call it from the same thread that called es_new_client. Most folks do both of these from the main thread.

IMPORTANT Because of these constraints, es_delete_client inherently races with event delivery.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for the answer! Yes, the ES is being planned to be used in daemon.

  1. As far as I know the number of ES clients is limited by the system. Are there any constant which defines this number? (headers)
  2. Can I create several ES clients in my daemon?
  3. Are there any limitations like a races in case of calling es_subscribe()/es_unsubscribe() in running client? Can I manage subscription by a fly?
  4. Can I use posix mutexs in functions which are called inside the block?
  5. Should we take care of signals while being in the block? Can/should we block some?
  6. Can we fork a new thread/process out of the block?

Thank you for support!

The number one thing that you need to make sure you do, no matter which approach you take, is to return a result BEFORE the deadline value in the es message. If you do not, the kernel WILL kill your process. So, if you block waiting for another thread/process to make a decision, make sure you have a way to return a result to the kernel if your processing takes longer than expected.

1. As far as I know the number of ES clients is limited by the system. Are there any constant which defines this number?

No.

I’m not actually sure if there is a fixed limit, but that shouldn’t matter because…

2. Can I create several ES clients in my daemon?

Yes.

However, doing this is questionable. You’d be better off having a single client and dynamically changing its settings.

3. Are there any limitations like a races in case of calling es_subscribe / es_unsubscribe in running client?

It’s hard to rule out any limitations but…

Can I manage subscription by a fly?

Yes.

4. Can I use posix mutexs in functions which are called inside the block?

Yes, but that’s a bad idea. If you must use a mutex, an OS unfair lock (start here) is a much better idea.

Regardless of what you do, it’s critical that you not block that thread for a long period of time. So, using an unfair lock to serialise access to a fast data structure is fine, but don’t go calling malloc within that lock.

5. Should we take care of signals while being in the block?

My general advice is that you not use signals for anything. There are much better IPC constructs on macOS.

6. Can we fork a new thread/process out of the block?

Probably, but see my response to question 4. This callback must be fast. If you’re creating a thread, or worse yet a process, from within this callback, you’re doing it wrong.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for clear cut explanation!