├── .gitignore ├── 01_mach_messages ├── CMakeLists.txt ├── alice_server.c └── bob_client.c ├── 02_mach_bidirectional ├── CMakeLists.txt ├── complex_port │ ├── alice_server.c │ ├── bob_client.c │ └── message.h └── reply_port │ ├── alice_server.c │ ├── bob_client.c │ └── message.h ├── 03_mach_ool_intro ├── CMakeLists.txt ├── alice_server.c ├── bob_client.c └── message.h ├── 04_mach_ool_vm ├── CMakeLists.txt ├── check_vm_fork.c ├── ool_data_transfer_options │ ├── alice_server.c │ ├── bob_client.c │ └── message.h ├── ool_virtual_copy_large │ ├── alice_server.c │ ├── bob_client.c │ └── message.h └── vm_utils.h ├── CMakeLists.txt ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .cache 3 | compile_commands.json 4 | -------------------------------------------------------------------------------- /01_mach_messages/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(alice_server alice_server.c) 2 | add_executable(bob_client bob_client.c) 3 | -------------------------------------------------------------------------------- /01_mach_messages/alice_server.c: -------------------------------------------------------------------------------- 1 | // Darwin 2 | #include 3 | #include 4 | #include 5 | 6 | // std 7 | #include 8 | #include 9 | 10 | typedef struct { 11 | mach_msg_header_t header; 12 | char bodyStr[32]; 13 | int bodyInt; 14 | 15 | // Suitable for use with the default trailer type - no custom trailer 16 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 17 | // `MACH_RCV_TRAILER_NULL` type. 18 | mach_msg_trailer_t trailer; 19 | } Message; 20 | 21 | mach_msg_return_t receive_msg(mach_port_name_t recvPort) { 22 | // Message buffer. 23 | Message message = {0}; 24 | 25 | mach_msg_return_t ret = mach_msg( 26 | /* msg */ (mach_msg_header_t *)&message, 27 | /* option */ MACH_RCV_MSG, 28 | /* send size */ 0, 29 | /* recv size */ sizeof(message), 30 | /* recv_name */ recvPort, 31 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 32 | /* notify port */ MACH_PORT_NULL); 33 | if (ret != MACH_MSG_SUCCESS) { 34 | return ret; 35 | } 36 | 37 | printf("got message!\n"); 38 | printf(" id : %d\n", message.header.msgh_id); 39 | printf(" bodyS : %s\n", message.bodyStr); 40 | printf(" bodyI : %d\n", message.bodyInt); 41 | 42 | return MACH_MSG_SUCCESS; 43 | } 44 | 45 | int main() { 46 | mach_port_t task = mach_task_self(); 47 | 48 | mach_port_name_t recvPort; 49 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &recvPort) != 50 | KERN_SUCCESS) { 51 | return EXIT_FAILURE; 52 | } 53 | 54 | if (mach_port_insert_right( 55 | task, recvPort, recvPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { 56 | return EXIT_FAILURE; 57 | } 58 | 59 | mach_port_t bootstrapPort; 60 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 61 | KERN_SUCCESS) { 62 | return EXIT_FAILURE; 63 | } 64 | 65 | if (bootstrap_register( 66 | bootstrapPort, "xyz.dmcyk.alice.as-a-service", recvPort) != 67 | KERN_SUCCESS) { 68 | return EXIT_FAILURE; 69 | } 70 | 71 | while (true) { 72 | mach_msg_return_t ret = receive_msg(recvPort); 73 | if (ret != MACH_MSG_SUCCESS) { 74 | printf("Failed to receive a message: %d\n", ret); 75 | } 76 | } 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /01_mach_messages/bob_client.c: -------------------------------------------------------------------------------- 1 | // Darwin 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // std 10 | #include 11 | #include 12 | #include 13 | 14 | typedef struct { 15 | mach_msg_header_t header; 16 | char bodyStr[32]; 17 | int bodyInt; 18 | } Message; 19 | 20 | int main() { 21 | mach_port_name_t task = mach_task_self(); 22 | 23 | mach_port_t bootstrapPort; 24 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 25 | KERN_SUCCESS) { 26 | return EXIT_FAILURE; 27 | } 28 | 29 | mach_port_t port; 30 | if (bootstrap_look_up(bootstrapPort, "xyz.dmcyk.alice.as-a-service", &port) != 31 | KERN_SUCCESS) { 32 | return EXIT_FAILURE; 33 | } 34 | 35 | Message message = {0}; 36 | message.header.msgh_remote_port = port; 37 | 38 | // copy send right on the remote port 39 | message.header.msgh_bits = MACH_MSGH_BITS_SET( 40 | /* remote */ MACH_MSG_TYPE_COPY_SEND, 41 | /* local */ 0, 42 | /* voucher */ 0, 43 | /* other */ 0); 44 | message.header.msgh_id = 4; 45 | message.header.msgh_size = sizeof(message); 46 | 47 | strcpy(message.bodyStr, "Hello Mach!"); 48 | message.bodyInt = 0xff; 49 | 50 | mach_msg_return_t ret = mach_msg( 51 | /* msg */ (mach_msg_header_t *)&message, 52 | /* option */ MACH_SEND_MSG, 53 | /* send size */ sizeof(message), 54 | /* recv size */ 0, 55 | /* recv_name */ MACH_PORT_NULL, 56 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 57 | /* notify port */ MACH_PORT_NULL); 58 | 59 | if (ret != MACH_MSG_SUCCESS) { 60 | printf("Failed mach_msg: %d\n", ret); 61 | return EXIT_FAILURE; 62 | } 63 | 64 | strcpy(message.bodyStr, "Hello Mach! #2"); 65 | 66 | ret = mach_msg( 67 | /* msg */ (mach_msg_header_t *)&message, 68 | /* option */ MACH_SEND_MSG, 69 | /* send size */ sizeof(message), 70 | /* recv size */ 0, 71 | /* recv_name */ MACH_PORT_NULL, 72 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 73 | /* notify port */ MACH_PORT_NULL); 74 | if (ret != MACH_MSG_SUCCESS) { 75 | printf("#2. Failed mach_msg: %d\n", ret); 76 | return EXIT_FAILURE; 77 | } 78 | 79 | // Make client sleep for a while to inspect ports using `lsmp`. 80 | sleep(60); 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /02_mach_bidirectional/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(reply_port_server reply_port/alice_server.c) 2 | add_executable(reply_port_client reply_port/bob_client.c) 3 | 4 | add_executable(complex_port_server complex_port/alice_server.c) 5 | add_executable(complex_port_client complex_port/bob_client.c) 6 | -------------------------------------------------------------------------------- /02_mach_bidirectional/complex_port/alice_server.c: -------------------------------------------------------------------------------- 1 | #include "message.h" 2 | 3 | // Darwin 4 | #include 5 | #include 6 | #include 7 | 8 | // std 9 | #include 10 | #include 11 | #include 12 | 13 | mach_msg_return_t send_reply(const PortMessage *inMessage) { 14 | Message response = {0}; 15 | 16 | response.header.msgh_bits = MACH_MSGH_BITS_SET( 17 | /* remote */ inMessage->descriptor.disposition, 18 | /* local */ 0, 19 | /* voucher */ 0, 20 | /* other */ 0); 21 | response.header.msgh_remote_port = inMessage->descriptor.name; 22 | response.header.msgh_id = MSG_ID_DEFAULT; 23 | response.header.msgh_size = sizeof(response); 24 | 25 | strcpy(response.bodyStr, "Response :) "); 26 | 27 | return mach_msg( 28 | /* msg */ (mach_msg_header_t *)&response, 29 | /* option */ MACH_SEND_MSG, 30 | /* send size */ sizeof(response), 31 | /* recv size */ 0, 32 | /* recv_name */ MACH_PORT_NULL, 33 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 34 | /* notify port */ MACH_PORT_NULL); 35 | } 36 | 37 | mach_msg_return_t receive_msg( 38 | mach_port_name_t recvPort, 39 | ReceiveAnyMessage *buffer) { 40 | mach_msg_return_t ret = mach_msg( 41 | /* msg */ (mach_msg_header_t *)buffer, 42 | /* option */ MACH_RCV_MSG, 43 | /* send size */ 0, 44 | /* recv size */ sizeof(*buffer), 45 | /* recv_name */ recvPort, 46 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 47 | /* notify port */ MACH_PORT_NULL); 48 | if (ret != MACH_MSG_SUCCESS) { 49 | return ret; 50 | } 51 | 52 | if (buffer->header.msgh_id == MSG_ID_DEFAULT) { 53 | Message *message = &buffer->message.message; 54 | 55 | printf("got default message!\n"); 56 | printf(" id : %d\n", message->header.msgh_id); 57 | printf(" bodyS : %s\n", message->bodyStr); 58 | printf(" bodyI : %d\n", message->bodyInt); 59 | } else if (buffer->header.msgh_id == MSG_ID_PORT) { 60 | printf( 61 | "got port message with name: %#x, disposition: %#x!\n", 62 | buffer->portMessage.message.descriptor.name, 63 | buffer->portMessage.message.descriptor.disposition); 64 | } else { 65 | return RCV_ERROR_INVALID_MESSAGE_ID; 66 | } 67 | 68 | return MACH_MSG_SUCCESS; 69 | } 70 | 71 | int main() { 72 | mach_port_t task = mach_task_self(); 73 | 74 | mach_port_name_t recvPort; 75 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &recvPort) != 76 | KERN_SUCCESS) { 77 | return EXIT_FAILURE; 78 | } 79 | 80 | if (mach_port_insert_right( 81 | task, recvPort, recvPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { 82 | return EXIT_FAILURE; 83 | } 84 | 85 | mach_port_t bootstrapPort; 86 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 87 | KERN_SUCCESS) { 88 | return EXIT_FAILURE; 89 | } 90 | 91 | if (bootstrap_register( 92 | bootstrapPort, "xyz.dmcyk.alice.as-a-service", recvPort) != 93 | KERN_SUCCESS) { 94 | return EXIT_FAILURE; 95 | } 96 | 97 | while (true) { 98 | // Message buffer. 99 | ReceiveAnyMessage receiveMessage = {0}; 100 | 101 | mach_msg_return_t ret = receive_msg(recvPort, &receiveMessage); 102 | if (ret != MACH_MSG_SUCCESS) { 103 | printf("Failed to receive a message: %#x\n", ret); 104 | continue; 105 | } 106 | 107 | // Continue if it's not the port descriptor message. 108 | if (receiveMessage.header.msgh_id != MSG_ID_PORT) { 109 | continue; 110 | } 111 | 112 | ret = send_reply(&receiveMessage.portMessage.message); 113 | 114 | if (ret != MACH_MSG_SUCCESS) { 115 | printf("Failed to respond: %#x\n", ret); 116 | } 117 | } 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /02_mach_bidirectional/complex_port/bob_client.c: -------------------------------------------------------------------------------- 1 | #include "message.h" 2 | 3 | // Darwin 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // std 12 | #include 13 | #include 14 | #include 15 | 16 | #define MS_IN_S 1000 17 | 18 | mach_msg_return_t 19 | receive_msg(mach_port_name_t recvPort, mach_msg_timeout_t timeout) { 20 | // Message buffer. 21 | ReceiveMessage receiveMessage = {0}; 22 | 23 | mach_msg_return_t ret = mach_msg( 24 | /* msg */ (mach_msg_header_t *)&receiveMessage, 25 | /* option */ MACH_RCV_MSG | MACH_RCV_TIMEOUT, 26 | /* send size */ 0, 27 | /* recv size */ sizeof(receiveMessage), 28 | /* recv_name */ recvPort, 29 | /* timeout */ timeout, 30 | /* notify port */ MACH_PORT_NULL); 31 | if (ret != MACH_MSG_SUCCESS) { 32 | return ret; 33 | } 34 | 35 | Message *message = &receiveMessage.message; 36 | 37 | printf("got response message!\n"); 38 | printf(" id : %d\n", message->header.msgh_id); 39 | printf(" bodyS : %s\n", message->bodyStr); 40 | printf(" bodyI : %d\n", message->bodyInt); 41 | 42 | return MACH_MSG_SUCCESS; 43 | } 44 | 45 | int main() { 46 | mach_port_name_t task = mach_task_self(); 47 | 48 | mach_port_t bootstrapPort; 49 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 50 | KERN_SUCCESS) { 51 | return EXIT_FAILURE; 52 | } 53 | 54 | mach_port_t port; 55 | if (bootstrap_look_up(bootstrapPort, "xyz.dmcyk.alice.as-a-service", &port) != 56 | KERN_SUCCESS) { 57 | return EXIT_FAILURE; 58 | } 59 | 60 | mach_port_t replyPort; 61 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &replyPort) != 62 | KERN_SUCCESS) { 63 | return EXIT_FAILURE; 64 | } 65 | 66 | if (mach_port_insert_right( 67 | task, replyPort, replyPort, MACH_MSG_TYPE_MAKE_SEND) != 68 | KERN_SUCCESS) { 69 | return EXIT_FAILURE; 70 | } 71 | 72 | PortMessage message = {0}; 73 | message.header.msgh_remote_port = port; 74 | 75 | // copy send right on the remote port 76 | message.header.msgh_bits = MACH_MSGH_BITS_SET( 77 | /* remote */ MACH_MSG_TYPE_COPY_SEND, 78 | /* local */ 0, 79 | /* voucher */ 0, 80 | /* other */ MACH_MSGH_BITS_COMPLEX); // Make sure not to forget to set 81 | // the complex bit! :) 82 | message.header.msgh_id = MSG_ID_PORT; 83 | message.header.msgh_size = sizeof(message); 84 | message.msgh_descriptor_count = 1; 85 | message.descriptor.name = replyPort; 86 | message.descriptor.disposition = MACH_MSG_TYPE_MAKE_SEND; 87 | message.descriptor.type = MACH_MSG_PORT_DESCRIPTOR; 88 | 89 | mach_msg_return_t ret = mach_msg( 90 | /* msg */ (mach_msg_header_t *)&message, 91 | /* option */ MACH_SEND_MSG, 92 | /* send size */ sizeof(message), 93 | /* recv size */ 0, 94 | /* recv_name */ MACH_PORT_NULL, 95 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 96 | /* notify port */ MACH_PORT_NULL); 97 | if (ret != MACH_MSG_SUCCESS) { 98 | printf("Failed mach_msg: %d\n", ret); 99 | return EXIT_FAILURE; 100 | } 101 | 102 | while (ret == MACH_MSG_SUCCESS) { 103 | ret = receive_msg(replyPort, /* timeout */ 2 * MS_IN_S); 104 | } 105 | 106 | if (ret == MACH_RCV_TIMED_OUT) { 107 | printf("Receive timed out, no more messages from alice yet.\n"); 108 | } else if (ret != MACH_MSG_SUCCESS) { 109 | printf("Failed to receive a message: %#x\n", ret); 110 | return 1; 111 | } 112 | 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /02_mach_bidirectional/complex_port/message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | mach_msg_header_t header; 7 | char bodyStr[32]; 8 | int bodyInt; 9 | } Message; 10 | 11 | typedef struct { 12 | Message message; 13 | 14 | // Suitable for use with the default trailer type - no custom trailer 15 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 16 | // `MACH_RCV_TRAILER_NULL` type. 17 | mach_msg_trailer_t trailer; 18 | } ReceiveMessage; 19 | 20 | typedef struct { 21 | mach_msg_header_t header; 22 | mach_msg_size_t msgh_descriptor_count; 23 | mach_msg_port_descriptor_t descriptor; 24 | } PortMessage; 25 | 26 | typedef struct { 27 | PortMessage message; 28 | 29 | // Suitable for use with the default trailer type - no custom trailer 30 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 31 | // `MACH_RCV_TRAILER_NULL` type. 32 | mach_msg_trailer_t trailer; 33 | } ReceivePortMessage; 34 | 35 | typedef union { 36 | mach_msg_header_t header; 37 | 38 | Message message; 39 | PortMessage portMessage; 40 | } AnyMesssage; 41 | 42 | typedef union { 43 | mach_msg_header_t header; 44 | 45 | ReceiveMessage message; 46 | ReceivePortMessage portMessage; 47 | } ReceiveAnyMessage; 48 | 49 | #define MSG_ID_DEFAULT 8 50 | #define MSG_ID_PORT 9 51 | 52 | #define RCV_ERROR_INVALID_MESSAGE_ID 0xffffff01 53 | -------------------------------------------------------------------------------- /02_mach_bidirectional/reply_port/alice_server.c: -------------------------------------------------------------------------------- 1 | #include "message.h" 2 | 3 | // Darwin 4 | #include 5 | #include 6 | #include 7 | 8 | // std 9 | #include 10 | #include 11 | 12 | mach_msg_return_t send_reply(mach_port_name_t port, const Message *inMessage) { 13 | Message response = {0}; 14 | response.header.msgh_bits = 15 | inMessage->header.msgh_bits & // received message already contains 16 | MACH_MSGH_BITS_REMOTE_MASK; // necessary remote bits to provide a 17 | // response, and it can differ 18 | // depending on SEND/SEND_ONCE right. 19 | 20 | response.header.msgh_remote_port = port; 21 | response.header.msgh_id = inMessage->header.msgh_id; 22 | response.header.msgh_size = sizeof(response); 23 | 24 | response.bodyInt = inMessage->bodyInt << 1; 25 | strcpy(response.bodyStr, "Response - "); 26 | strncpy( 27 | response.bodyStr + strlen(response.bodyStr), 28 | inMessage->bodyStr, 29 | sizeof(response.bodyStr) - strlen(response.bodyStr) - 1); 30 | 31 | return mach_msg( 32 | /* msg */ (mach_msg_header_t *)&response, 33 | /* option */ MACH_SEND_MSG, 34 | /* send size */ sizeof(response), 35 | /* recv size */ 0, 36 | /* recv_name */ MACH_PORT_NULL, 37 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 38 | /* notify port */ MACH_PORT_NULL); 39 | } 40 | 41 | mach_msg_return_t 42 | receive_msg(mach_port_name_t recvPort, ReceiveMessage *buffer) { 43 | mach_msg_return_t ret = mach_msg( 44 | /* msg */ (mach_msg_header_t *)buffer, 45 | /* option */ MACH_RCV_MSG, 46 | /* send size */ 0, 47 | /* recv size */ sizeof(*buffer), 48 | /* recv_name */ recvPort, 49 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 50 | /* notify port */ MACH_PORT_NULL); 51 | if (ret != MACH_MSG_SUCCESS) { 52 | return ret; 53 | } 54 | 55 | Message *message = &buffer->message; 56 | 57 | printf("got message!\n"); 58 | printf(" id : %d\n", message->header.msgh_id); 59 | printf(" bodyS : %s\n", message->bodyStr); 60 | printf(" bodyI : %d\n", message->bodyInt); 61 | 62 | return MACH_MSG_SUCCESS; 63 | } 64 | 65 | int main() { 66 | mach_port_t task = mach_task_self(); 67 | 68 | mach_port_name_t recvPort; 69 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &recvPort) != 70 | KERN_SUCCESS) { 71 | return EXIT_FAILURE; 72 | } 73 | 74 | if (mach_port_insert_right( 75 | task, recvPort, recvPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { 76 | return EXIT_FAILURE; 77 | } 78 | 79 | mach_port_t bootstrapPort; 80 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 81 | KERN_SUCCESS) { 82 | return EXIT_FAILURE; 83 | } 84 | 85 | if (bootstrap_register( 86 | bootstrapPort, "xyz.dmcyk.alice.as-a-service", recvPort) != 87 | KERN_SUCCESS) { 88 | return EXIT_FAILURE; 89 | } 90 | 91 | while (true) { 92 | // Message buffer. 93 | ReceiveMessage receiveMessage = {0}; 94 | 95 | mach_msg_return_t ret = receive_msg(recvPort, &receiveMessage); 96 | if (ret != MACH_MSG_SUCCESS) { 97 | printf("Failed to receive a message: %#x\n", ret); 98 | continue; 99 | } 100 | 101 | // Continue if there's no reply port. 102 | if (receiveMessage.message.header.msgh_remote_port == MACH_PORT_NULL) { 103 | continue; 104 | } 105 | 106 | // Send a response. 107 | ret = send_reply( 108 | receiveMessage.message.header.msgh_remote_port, 109 | &receiveMessage.message); 110 | 111 | if (ret != MACH_MSG_SUCCESS) { 112 | printf("Failed to respond: %#x\n", ret); 113 | } 114 | } 115 | 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /02_mach_bidirectional/reply_port/bob_client.c: -------------------------------------------------------------------------------- 1 | #include "message.h" 2 | 3 | // Darwin 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // std 12 | #include 13 | #include 14 | #include 15 | 16 | #define MS_IN_S 1000 17 | 18 | mach_msg_return_t 19 | receive_msg(mach_port_name_t recvPort, mach_msg_timeout_t timeout) { 20 | // Message buffer. 21 | ReceiveMessage receiveMessage = {0}; 22 | 23 | mach_msg_return_t ret = mach_msg( 24 | /* msg */ (mach_msg_header_t *)&receiveMessage, 25 | /* option */ MACH_RCV_MSG | MACH_RCV_TIMEOUT, 26 | /* send size */ 0, 27 | /* recv size */ sizeof(receiveMessage), 28 | /* recv_name */ recvPort, 29 | /* timeout */ timeout, 30 | /* notify port */ MACH_PORT_NULL); 31 | if (ret != MACH_MSG_SUCCESS) { 32 | return ret; 33 | } 34 | 35 | Message *message = &receiveMessage.message; 36 | 37 | printf("got response message!\n"); 38 | printf(" id : %d\n", message->header.msgh_id); 39 | printf(" bodyS : %s\n", message->bodyStr); 40 | printf(" bodyI : %d\n", message->bodyInt); 41 | 42 | return MACH_MSG_SUCCESS; 43 | } 44 | 45 | int main() { 46 | mach_port_name_t task = mach_task_self(); 47 | 48 | mach_port_t bootstrapPort; 49 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 50 | KERN_SUCCESS) { 51 | return EXIT_FAILURE; 52 | } 53 | 54 | mach_port_t port; 55 | if (bootstrap_look_up(bootstrapPort, "xyz.dmcyk.alice.as-a-service", &port) != 56 | KERN_SUCCESS) { 57 | return EXIT_FAILURE; 58 | } 59 | 60 | mach_port_t replyPort; 61 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &replyPort) != 62 | KERN_SUCCESS) { 63 | return EXIT_FAILURE; 64 | } 65 | 66 | if (mach_port_insert_right( 67 | task, replyPort, replyPort, MACH_MSG_TYPE_MAKE_SEND) != 68 | KERN_SUCCESS) { 69 | return EXIT_FAILURE; 70 | } 71 | 72 | Message message = {0}; 73 | message.header.msgh_remote_port = port; 74 | message.header.msgh_local_port = replyPort; 75 | 76 | // Copy send right on the remote port 77 | // Make send_once right on the local port 78 | message.header.msgh_bits = MACH_MSGH_BITS_SET( 79 | /* remote */ MACH_MSG_TYPE_COPY_SEND, 80 | /* local */ MACH_MSG_TYPE_MAKE_SEND, 81 | /* voucher */ 0, 82 | /* other */ 0); 83 | message.header.msgh_id = 4; 84 | message.header.msgh_size = sizeof(message); 85 | 86 | strcpy(message.bodyStr, "Hello Mach!"); 87 | message.bodyInt = 0xff; 88 | 89 | mach_msg_return_t ret = mach_msg( 90 | /* msg */ (mach_msg_header_t *)&message, 91 | /* option */ MACH_SEND_MSG, 92 | /* send size */ sizeof(message), 93 | /* recv size */ 0, 94 | /* recv_name */ MACH_PORT_NULL, 95 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 96 | /* notify port */ MACH_PORT_NULL); 97 | 98 | if (ret != MACH_MSG_SUCCESS) { 99 | printf("Failed mach_msg: %d\n", ret); 100 | return EXIT_FAILURE; 101 | } 102 | 103 | strcpy(message.bodyStr, "Hello Mach! #2"); 104 | 105 | ret = mach_msg( 106 | /* msg */ (mach_msg_header_t *)&message, 107 | /* option */ MACH_SEND_MSG, 108 | /* send size */ sizeof(message), 109 | /* recv size */ 0, 110 | /* recv_name */ MACH_PORT_NULL, 111 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 112 | /* notify port */ MACH_PORT_NULL); 113 | if (ret != MACH_MSG_SUCCESS) { 114 | printf("#2. Failed mach_msg: %d\n", ret); 115 | return EXIT_FAILURE; 116 | } 117 | 118 | while (ret == MACH_MSG_SUCCESS) { 119 | ret = receive_msg(replyPort, /* timeout */ 1 * MS_IN_S); 120 | } 121 | 122 | if (ret == MACH_RCV_TIMED_OUT) { 123 | printf("Receive timed out, no more messages from alice yet.\n"); 124 | } else if (ret != MACH_MSG_SUCCESS) { 125 | printf("Failed to receive a message: %#x\n", ret); 126 | return 1; 127 | } 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /02_mach_bidirectional/reply_port/message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | mach_msg_header_t header; 7 | char bodyStr[32]; 8 | int bodyInt; 9 | } Message; 10 | 11 | typedef struct { 12 | Message message; 13 | 14 | // Suitable for use with the default trailer type - no custom trailer 15 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 16 | // `MACH_RCV_TRAILER_NULL` type. 17 | mach_msg_trailer_t trailer; 18 | } ReceiveMessage; 19 | -------------------------------------------------------------------------------- /03_mach_ool_intro/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(complex_ool_intro_server alice_server.c) 2 | add_executable(complex_ool_intro_client bob_client.c) 3 | -------------------------------------------------------------------------------- /03_mach_ool_intro/alice_server.c: -------------------------------------------------------------------------------- 1 | #include "message.h" 2 | 3 | // Darwin 4 | #include 5 | #include 6 | #include 7 | 8 | // std 9 | #include 10 | #include 11 | 12 | mach_msg_return_t send_reply(mach_port_name_t port, const Message *inMessage) { 13 | Message response = {0}; 14 | 15 | response.header.msgh_bits = 16 | inMessage->header.msgh_bits & // received message already contains 17 | MACH_MSGH_BITS_REMOTE_MASK; // necessary remote bits to provide a 18 | // response, and it can differ 19 | // depending on SEND/SEND_ONCE right. 20 | 21 | response.header.msgh_remote_port = port; 22 | response.header.msgh_id = inMessage->header.msgh_id; 23 | response.header.msgh_size = sizeof(response); 24 | 25 | response.bodyInt = inMessage->bodyInt << 1; 26 | strcpy(response.bodyStr, "Response - "); 27 | strncpy( 28 | response.bodyStr + strlen(response.bodyStr), 29 | inMessage->bodyStr, 30 | sizeof(response.bodyStr) - strlen(response.bodyStr) - 1); 31 | 32 | return mach_msg( 33 | /* msg */ (mach_msg_header_t *)&response, 34 | /* option */ MACH_SEND_MSG, 35 | /* send size */ sizeof(response), 36 | /* recv size */ 0, 37 | /* recv_name */ MACH_PORT_NULL, 38 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 39 | /* notify port */ MACH_PORT_NULL); 40 | } 41 | 42 | mach_msg_return_t send_ool_reply( 43 | mach_port_name_t port, 44 | const Message *inMessage, 45 | void *addr, 46 | mach_msg_size_t size) { 47 | OOLMessage message = {0}; 48 | message.header.msgh_bits = MACH_MSGH_BITS_SET( 49 | /* remote */ MACH_MSG_TYPE_COPY_SEND, 50 | /* local */ 0, 51 | /* voucher */ 0, 52 | /* other */ MACH_MSGH_BITS_COMPLEX); 53 | message.header.msgh_remote_port = port; 54 | message.header.msgh_id = inMessage->header.msgh_id; 55 | message.header.msgh_size = sizeof(message); 56 | message.msgh_descriptor_count = 1; 57 | 58 | message.descriptor.address = addr; 59 | message.descriptor.size = size; 60 | message.descriptor.copy = MACH_MSG_VIRTUAL_COPY; 61 | message.descriptor.deallocate = false; 62 | message.descriptor.type = MACH_MSG_OOL_DESCRIPTOR; 63 | 64 | return mach_msg( 65 | /* msg */ (mach_msg_header_t *)&message, 66 | /* option */ MACH_SEND_MSG, 67 | /* send size */ sizeof(message), 68 | /* recv size */ 0, 69 | /* recv_name */ MACH_PORT_NULL, 70 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 71 | /* notify port */ MACH_PORT_NULL); 72 | } 73 | 74 | mach_msg_return_t 75 | receive_msg(mach_port_name_t recvPort, ReceiveMessage *buffer) { 76 | mach_msg_return_t ret = mach_msg( 77 | /* msg */ (mach_msg_header_t *)buffer, 78 | /* option */ MACH_RCV_MSG, 79 | /* send size */ 0, 80 | /* recv size */ sizeof(*buffer), 81 | /* recv_name */ recvPort, 82 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 83 | /* notify port */ MACH_PORT_NULL); 84 | if (ret != MACH_MSG_SUCCESS) { 85 | return ret; 86 | } 87 | 88 | Message *message = &buffer->message; 89 | 90 | printf("got message!\n"); 91 | printf(" id : %d\n", message->header.msgh_id); 92 | printf(" bodyS : %s\n", message->bodyStr); 93 | printf(" bodyI : %d\n", message->bodyInt); 94 | 95 | return MACH_MSG_SUCCESS; 96 | } 97 | 98 | void fill_pages(void *start, void *end, const char *data) { 99 | while (start < end) { 100 | strcpy((char *)start, data); 101 | 102 | start = (void *)((vm_address_t)start + vm_page_size); 103 | uint32_t *pageEnd = (uint32_t *)start; 104 | *(pageEnd - 1) = arc4random(); 105 | } 106 | } 107 | 108 | int main() { 109 | mach_port_t task = mach_task_self(); 110 | 111 | mach_port_name_t recvPort; 112 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &recvPort) != 113 | KERN_SUCCESS) { 114 | return EXIT_FAILURE; 115 | } 116 | 117 | if (mach_port_insert_right( 118 | task, recvPort, recvPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { 119 | return EXIT_FAILURE; 120 | } 121 | 122 | mach_port_t bootstrapPort; 123 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 124 | KERN_SUCCESS) { 125 | return EXIT_FAILURE; 126 | } 127 | 128 | if (bootstrap_register( 129 | bootstrapPort, "xyz.dmcyk.alice.as-a-service", recvPort) != 130 | KERN_SUCCESS) { 131 | return EXIT_FAILURE; 132 | } 133 | 134 | void *oolBuffer = NULL; 135 | vm_size_t oolBufferSize = vm_page_size; 136 | if (vm_allocate( 137 | mach_task_self(), 138 | (vm_address_t *)&oolBuffer, 139 | oolBufferSize, 140 | VM_PROT_READ | VM_PROT_WRITE) != KERN_SUCCESS) { 141 | printf("Failed to allocate memory buffer\n"); 142 | return 1; 143 | } 144 | 145 | strcpy((char *)oolBuffer, "Hello, OOL data!"); 146 | 147 | while (true) { 148 | // Message buffer. 149 | ReceiveMessage receiveMessage = {0}; 150 | 151 | mach_msg_return_t ret = receive_msg(recvPort, &receiveMessage); 152 | if (ret != MACH_MSG_SUCCESS) { 153 | printf("Failed to receive a message: %#x\n", ret); 154 | continue; 155 | } 156 | 157 | // Continue if there's no reply port. 158 | if (receiveMessage.message.header.msgh_remote_port == MACH_PORT_NULL) { 159 | continue; 160 | } 161 | 162 | switch (receiveMessage.message.header.msgh_id) { 163 | case MSG_ID_COPY_MEM: 164 | // As per request, respond with OOL memory. 165 | ret = send_ool_reply( 166 | receiveMessage.message.header.msgh_remote_port, 167 | &receiveMessage.message, 168 | oolBuffer, 169 | oolBufferSize); 170 | break; 171 | 172 | default: 173 | // Send a generic, inline response. 174 | ret = send_reply( 175 | receiveMessage.message.header.msgh_remote_port, 176 | &receiveMessage.message); 177 | break; 178 | } 179 | 180 | if (ret != MACH_MSG_SUCCESS) { 181 | printf("Failed to respond: %#x\n", ret); 182 | } 183 | } 184 | 185 | return 0; 186 | } 187 | -------------------------------------------------------------------------------- /03_mach_ool_intro/bob_client.c: -------------------------------------------------------------------------------- 1 | #include "message.h" 2 | 3 | // Darwin 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | // std 16 | #include 17 | #include 18 | 19 | #define MS_IN_S 1000 20 | 21 | mach_msg_return_t 22 | receive_msg(mach_port_name_t recvPort, mach_msg_timeout_t timeout) { 23 | // Message buffer. 24 | ReceiveMessage receiveMessage = {0}; 25 | 26 | mach_msg_return_t ret = mach_msg( 27 | /* msg */ (mach_msg_header_t *)&receiveMessage, 28 | /* option */ MACH_RCV_MSG | MACH_RCV_TIMEOUT, 29 | /* send size */ 0, 30 | /* recv size */ sizeof(receiveMessage), 31 | /* recv_name */ recvPort, 32 | /* timeout */ timeout, 33 | /* notify port */ MACH_PORT_NULL); 34 | if (ret != MACH_MSG_SUCCESS) { 35 | return ret; 36 | } 37 | 38 | if (receiveMessage.message.header.msgh_id != MSG_ID_DEFAULT) { 39 | return RCV_ERROR_INVALID_MESSAGE_ID; 40 | } 41 | 42 | Message *message = &receiveMessage.message; 43 | 44 | printf("got response message!\n"); 45 | printf(" id : %d\n", message->header.msgh_id); 46 | printf(" bodyS : %s\n", message->bodyStr); 47 | printf(" bodyI : %d\n", message->bodyInt); 48 | 49 | return MACH_MSG_SUCCESS; 50 | } 51 | 52 | mach_msg_return_t 53 | receive_ool_message(mach_port_name_t recvPort, OOLReceiveMessage *rcvMessage) { 54 | mach_msg_return_t ret = mach_msg( 55 | /* msg */ (mach_msg_header_t *)rcvMessage, 56 | /* option */ MACH_RCV_MSG, 57 | /* send size */ 0, 58 | /* recv size */ sizeof(*rcvMessage), 59 | /* recv_name */ recvPort, 60 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 61 | /* notify port */ MACH_PORT_NULL); 62 | if (ret != MACH_MSG_SUCCESS) { 63 | return ret; 64 | } 65 | 66 | if (rcvMessage->message.header.msgh_id != MSG_ID_COPY_MEM) { 67 | return RCV_ERROR_INVALID_MESSAGE_ID; 68 | } 69 | 70 | return MACH_MSG_SUCCESS; 71 | } 72 | 73 | static const char *mach_copy_option_to_str(mach_msg_copy_options_t option) { 74 | switch (option) { 75 | case MACH_MSG_PHYSICAL_COPY: 76 | return "MACH_MSG_PHYSICAL_COPY"; 77 | case MACH_MSG_VIRTUAL_COPY: 78 | return "MACH_MSG_VIRTUAL_COPY"; 79 | default: 80 | return "unknown"; 81 | } 82 | } 83 | 84 | int main() { 85 | mach_port_name_t task = mach_task_self(); 86 | 87 | mach_port_t bootstrapPort; 88 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 89 | KERN_SUCCESS) { 90 | return EXIT_FAILURE; 91 | } 92 | 93 | mach_port_t port; 94 | if (bootstrap_look_up(bootstrapPort, "xyz.dmcyk.alice.as-a-service", &port) != 95 | KERN_SUCCESS) { 96 | return EXIT_FAILURE; 97 | } 98 | 99 | mach_port_t replyPort; 100 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &replyPort) != 101 | KERN_SUCCESS) { 102 | return EXIT_FAILURE; 103 | } 104 | 105 | if (mach_port_insert_right( 106 | task, replyPort, replyPort, MACH_MSG_TYPE_MAKE_SEND) != 107 | KERN_SUCCESS) { 108 | return EXIT_FAILURE; 109 | } 110 | 111 | Message message = {0}; 112 | message.header.msgh_remote_port = port; 113 | message.header.msgh_local_port = replyPort; 114 | 115 | // Setup message rights. 116 | message.header.msgh_bits = MACH_MSGH_BITS_SET( 117 | /* remote */ MACH_MSG_TYPE_COPY_SEND, 118 | /* local */ MACH_MSG_TYPE_MAKE_SEND, 119 | /* voucher */ 0, 120 | /* other */ 0); 121 | message.header.msgh_id = MSG_ID_DEFAULT; 122 | message.header.msgh_size = sizeof(message); 123 | 124 | strcpy(message.bodyStr, "Hello Mach!"); 125 | message.bodyInt = 0xff; 126 | 127 | mach_msg_return_t ret = mach_msg( 128 | /* msg */ (mach_msg_header_t *)&message, 129 | /* option */ MACH_SEND_MSG, 130 | /* send size */ sizeof(message), 131 | /* recv size */ 0, 132 | /* recv_name */ MACH_PORT_NULL, 133 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 134 | /* notify port */ MACH_PORT_NULL); 135 | 136 | if (ret != MACH_MSG_SUCCESS) { 137 | printf("Failed mach_msg: %d\n", ret); 138 | return EXIT_FAILURE; 139 | } 140 | 141 | while (ret == MACH_MSG_SUCCESS) { 142 | ret = receive_msg(replyPort, /* timeout */ 0.5 * MS_IN_S); 143 | } 144 | 145 | if (ret == MACH_RCV_TIMED_OUT) { 146 | // Just a timeout, continue. 147 | } else if (ret != MACH_MSG_SUCCESS) { 148 | printf("Failed to receive a message: %#x\n", ret); 149 | return 1; 150 | } 151 | 152 | strcpy(message.bodyStr, "Request OOL"); 153 | message.header.msgh_id = MSG_ID_COPY_MEM; 154 | ret = mach_msg( 155 | /* msg */ (mach_msg_header_t *)&message, 156 | /* option */ MACH_SEND_MSG, 157 | /* send size */ sizeof(message), 158 | /* recv size */ 0, 159 | /* recv_name */ MACH_PORT_NULL, 160 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 161 | /* notify port */ MACH_PORT_NULL); 162 | if (ret != MACH_MSG_SUCCESS) { 163 | printf("Failed mach_msg: %d\n", ret); 164 | return EXIT_FAILURE; 165 | } 166 | 167 | while (true) { 168 | OOLReceiveMessage rcvMessage = {0}; 169 | ret = receive_ool_message(replyPort, &rcvMessage); 170 | if (ret != MACH_MSG_SUCCESS) { 171 | printf("Failed to receive an OOL message: %#x\n", ret); 172 | return 1; 173 | } 174 | 175 | printf( 176 | "%s\n" 177 | " buffer addr: %p\n" 178 | " total buffer size: %#x\n" 179 | " copy option: %s\n", 180 | (const char *)rcvMessage.message.descriptor.address, 181 | rcvMessage.message.descriptor.address, 182 | rcvMessage.message.descriptor.size, 183 | mach_copy_option_to_str(rcvMessage.message.descriptor.copy)); 184 | } 185 | 186 | return 0; 187 | } 188 | -------------------------------------------------------------------------------- /03_mach_ool_intro/message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | mach_msg_header_t header; 7 | char bodyStr[32]; 8 | int bodyInt; 9 | } Message; 10 | 11 | typedef struct { 12 | Message message; 13 | 14 | // Suitable for use with the default trailer type - no custom trailer 15 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 16 | // `MACH_RCV_TRAILER_NULL` type. 17 | mach_msg_trailer_t trailer; 18 | } ReceiveMessage; 19 | 20 | typedef struct { 21 | mach_msg_header_t header; 22 | mach_msg_size_t msgh_descriptor_count; 23 | mach_msg_ool_descriptor_t descriptor; 24 | } OOLMessage; 25 | 26 | typedef struct { 27 | OOLMessage message; 28 | 29 | // Suitable for use with the default trailer type - no custom trailer 30 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 31 | // `MACH_RCV_TRAILER_NULL` type. 32 | mach_msg_trailer_t trailer; 33 | } OOLReceiveMessage; 34 | 35 | #define MSG_ID_DEFAULT 8 36 | #define MSG_ID_COPY_MEM 10 37 | 38 | #define RCV_ERROR_INVALID_MESSAGE_ID 0xffffff01 39 | -------------------------------------------------------------------------------- /04_mach_ool_vm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(ool_data_transfer_options_server ool_data_transfer_options/alice_server.c) 2 | add_executable(ool_data_transfer_options_client ool_data_transfer_options/bob_client.c) 3 | 4 | add_executable(ool_virtual_copy_large_server ool_virtual_copy_large/alice_server.c) 5 | add_executable(ool_virtual_copy_large_client ool_virtual_copy_large/bob_client.c) 6 | 7 | add_executable(check_vm_fork check_vm_fork.c) 8 | -------------------------------------------------------------------------------- /04_mach_ool_vm/check_vm_fork.c: -------------------------------------------------------------------------------- 1 | #include "vm_utils.h" 2 | 3 | // std 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define VM_REGION_DUMP vm_region_dump_submap_info 11 | 12 | int main() { 13 | vm_address_t _oolBuffer = 0; 14 | void *oolBuffer = 0; 15 | vm_size_t oolBufferSize = vm_page_size; 16 | 17 | if (vm_map( 18 | mach_task_self(), 19 | &_oolBuffer, 20 | oolBufferSize, 21 | /* mask */ 0, 22 | VM_FLAGS_ANYWHERE, 23 | MACH_PORT_NULL, 24 | 0, 25 | /* copy */ false, 26 | VM_PROT_DEFAULT, 27 | VM_PROT_ALL, 28 | VM_INHERIT_COPY) != KERN_SUCCESS) { 29 | return 1; 30 | } 31 | 32 | oolBuffer = (void *)_oolBuffer; 33 | 34 | VM_REGION_DUMP(oolBuffer); 35 | strcpy((char *)oolBuffer, "foo"); 36 | 37 | printf("\nregion after write\n"); 38 | VM_REGION_DUMP(oolBuffer); 39 | 40 | printf("\nstart fork\n\n"); 41 | 42 | if (fork() == 0) { 43 | printf("child pid: %d\n", getpid()); 44 | 45 | VM_REGION_DUMP(oolBuffer); 46 | 47 | strcpy((char *)oolBuffer, "child"); 48 | 49 | printf("\nregion after write\n"); 50 | VM_REGION_DUMP(oolBuffer); 51 | printf("\n"); 52 | } else { 53 | sleep(1); 54 | 55 | printf("parent pid: %d\n", getpid()); 56 | printf("parent print buffer: %s\n", (char *)oolBuffer); 57 | 58 | VM_REGION_DUMP(oolBuffer); 59 | 60 | strcpy((char *)oolBuffer, "child"); 61 | 62 | printf("\nregion after write\n"); 63 | VM_REGION_DUMP(oolBuffer); 64 | } 65 | 66 | // Sleep at the end so either of the processes doesn't get killed before all 67 | // the region cases are dumped. 68 | sleep(2); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /04_mach_ool_vm/ool_data_transfer_options/alice_server.c: -------------------------------------------------------------------------------- 1 | #include "../vm_utils.h" 2 | #include "message.h" 3 | 4 | // Darwin 5 | #include 6 | #include 7 | #include 8 | 9 | // std 10 | #include 11 | #include 12 | 13 | mach_msg_return_t send_reply(mach_port_name_t port, const Message *inMessage) { 14 | Message response = {0}; 15 | 16 | response.header.msgh_bits = 17 | inMessage->header.msgh_bits & // received message already contains 18 | MACH_MSGH_BITS_REMOTE_MASK; // necessary remote bits to provide a 19 | // response, and it can differ 20 | // depending on SEND/SEND_ONCE right. 21 | 22 | response.header.msgh_remote_port = port; 23 | response.header.msgh_id = inMessage->header.msgh_id; 24 | response.header.msgh_size = sizeof(response); 25 | 26 | response.bodyInt = inMessage->bodyInt << 1; 27 | strcpy(response.bodyStr, "Response - "); 28 | strncpy( 29 | response.bodyStr + strlen(response.bodyStr), 30 | inMessage->bodyStr, 31 | sizeof(response.bodyStr) - strlen(response.bodyStr) - 1); 32 | 33 | return mach_msg( 34 | /* msg */ (mach_msg_header_t *)&response, 35 | /* option */ MACH_SEND_MSG, 36 | /* send size */ sizeof(response), 37 | /* recv size */ 0, 38 | /* recv_name */ MACH_PORT_NULL, 39 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 40 | /* notify port */ MACH_PORT_NULL); 41 | } 42 | 43 | mach_msg_return_t send_ool_reply( 44 | mach_port_name_t port, 45 | const Message *inMessage, 46 | void *addr, 47 | mach_msg_size_t size, 48 | mach_msg_copy_options_t copy, 49 | boolean_t deallocate) { 50 | OOLMessage message = {0}; 51 | message.header.msgh_bits = MACH_MSGH_BITS_SET( 52 | /* remote */ MACH_MSG_TYPE_COPY_SEND, 53 | /* local */ 0, 54 | /* voucher */ 0, 55 | /* other */ MACH_MSGH_BITS_COMPLEX); 56 | message.header.msgh_remote_port = port; 57 | message.header.msgh_id = inMessage->header.msgh_id; 58 | message.header.msgh_size = sizeof(message); 59 | message.msgh_descriptor_count = 1; 60 | 61 | message.descriptor.address = addr; 62 | message.descriptor.size = size; 63 | message.descriptor.copy = copy; 64 | message.descriptor.deallocate = deallocate; 65 | message.descriptor.type = MACH_MSG_OOL_DESCRIPTOR; 66 | 67 | return mach_msg( 68 | /* msg */ (mach_msg_header_t *)&message, 69 | /* option */ MACH_SEND_MSG, 70 | /* send size */ sizeof(message), 71 | /* recv size */ 0, 72 | /* recv_name */ MACH_PORT_NULL, 73 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 74 | /* notify port */ MACH_PORT_NULL); 75 | } 76 | 77 | mach_msg_return_t 78 | receive_msg(mach_port_name_t recvPort, ReceiveMessage *buffer) { 79 | mach_msg_return_t ret = mach_msg( 80 | /* msg */ (mach_msg_header_t *)buffer, 81 | /* option */ MACH_RCV_MSG, 82 | /* send size */ 0, 83 | /* recv size */ sizeof(*buffer), 84 | /* recv_name */ recvPort, 85 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 86 | /* notify port */ MACH_PORT_NULL); 87 | if (ret != MACH_MSG_SUCCESS) { 88 | return ret; 89 | } 90 | 91 | Message *message = &buffer->message; 92 | 93 | printf("got message!\n"); 94 | printf(" id : %d\n", message->header.msgh_id); 95 | printf(" bodyS : %s\n", message->bodyStr); 96 | printf(" bodyI : %d\n", message->bodyInt); 97 | 98 | return MACH_MSG_SUCCESS; 99 | } 100 | 101 | void fill_pages(void *start, void *end, const char *data) { 102 | while (start < end) { 103 | strcpy((char *)start, data); 104 | 105 | start = (void *)((vm_address_t)start + vm_page_size); 106 | uint32_t *pageEnd = (uint32_t *)start; 107 | *(pageEnd - 1) = arc4random(); 108 | } 109 | } 110 | 111 | kern_return_t 112 | send_memory_inspection_messages(const ReceiveMessage *receiveMessage) { 113 | typedef struct { 114 | const char *name; 115 | vm_size_t size; 116 | mach_msg_copy_options_t copy; 117 | boolean_t deallocate; 118 | boolean_t fill_all; 119 | } MemoryOptions; 120 | 121 | MemoryOptions testCases[] = { 122 | // virtual copy, single page 123 | {.name = "VIRTUAL;PAGE;NO_FREE", 124 | .size = vm_page_size, 125 | .copy = MACH_MSG_VIRTUAL_COPY, 126 | .deallocate = false}, 127 | {.name = "VIRTUAL;PAGE;FREE", 128 | .size = vm_page_size, 129 | .copy = MACH_MSG_VIRTUAL_COPY, 130 | .deallocate = true}, 131 | 132 | // virtual copy, multiple pages 133 | {.name = "VIRTUAL;PAGEx16;NO_FREE", 134 | .size = vm_page_size * 16, 135 | .copy = MACH_MSG_VIRTUAL_COPY, 136 | .deallocate = false}, 137 | {.name = "VIRTUAL;PAGEx16;FREE", 138 | .size = vm_page_size * 16, 139 | .copy = MACH_MSG_VIRTUAL_COPY, 140 | .deallocate = true}, 141 | 142 | // large virtual data 143 | {.name = "VIRTUAL;256MB;NO_FREE", 144 | .size = /* 256 MB */ 268435456, 145 | .copy = MACH_MSG_VIRTUAL_COPY, 146 | .deallocate = false, 147 | .fill_all = true}, 148 | 149 | // physical copy, single page 150 | {.name = "PHYSICAL;PAGE;NO_FREE", 151 | .size = vm_page_size, 152 | .copy = MACH_MSG_PHYSICAL_COPY, 153 | .deallocate = false}, 154 | {.name = "PHYSICAL;PAGE;FREE", 155 | .size = vm_page_size, 156 | .copy = MACH_MSG_PHYSICAL_COPY, 157 | .deallocate = true}, 158 | 159 | // physical copy, multiple pages 160 | {.name = "PHYSICAL;PAGEx16;NO_FREE", 161 | .size = vm_page_size * 16, 162 | .copy = MACH_MSG_PHYSICAL_COPY, 163 | .deallocate = false}, 164 | {.name = "PHYSICAL;PAGEx16;FREE", 165 | .size = vm_page_size * 16, 166 | .copy = MACH_MSG_PHYSICAL_COPY, 167 | .deallocate = true}, 168 | }; 169 | 170 | for (int i = 0; i < sizeof(testCases) / sizeof(testCases[0]); ++i) { 171 | MemoryOptions testCase = testCases[i]; 172 | 173 | void *oolBuffer = NULL; 174 | vm_size_t oolBufferSize = testCase.size; 175 | if (vm_allocate( 176 | mach_task_self(), 177 | (vm_address_t *)&oolBuffer, 178 | oolBufferSize, 179 | VM_PROT_READ | VM_PROT_WRITE) != KERN_SUCCESS) { 180 | printf("Failed to allocate memory buffer\n"); 181 | return KERN_FAILURE; 182 | } 183 | 184 | void *oolBufferEnd = (void *)((vm_address_t)oolBuffer + testCase.size); 185 | 186 | // Copy test case name. 187 | strcpy((char *)oolBuffer, testCase.name); 188 | 189 | if (testCase.fill_all) { 190 | fill_pages(oolBuffer, oolBufferEnd, testCase.name); 191 | } 192 | 193 | kern_return_t ret = send_ool_reply( 194 | receiveMessage->message.header.msgh_remote_port, 195 | &receiveMessage->message, 196 | oolBuffer, 197 | oolBufferSize, 198 | testCase.copy, 199 | testCase.deallocate); 200 | printf("sent test case message: #%d, res - %d\n", i + 1, ret); 201 | 202 | // Stop if deallocated, the address is no longer valid here. 203 | if (testCase.deallocate) { 204 | continue; 205 | } 206 | 207 | vm_region_dump_submap_info(oolBuffer); 208 | 209 | if (testCase.fill_all) { 210 | fill_pages(oolBuffer, oolBufferEnd, "some other data\n"); 211 | } 212 | } 213 | 214 | return KERN_SUCCESS; 215 | } 216 | 217 | int main() { 218 | mach_port_t task = mach_task_self(); 219 | 220 | mach_port_name_t recvPort; 221 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &recvPort) != 222 | KERN_SUCCESS) { 223 | return EXIT_FAILURE; 224 | } 225 | 226 | if (mach_port_insert_right( 227 | task, recvPort, recvPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { 228 | return EXIT_FAILURE; 229 | } 230 | 231 | mach_port_t bootstrapPort; 232 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 233 | KERN_SUCCESS) { 234 | return EXIT_FAILURE; 235 | } 236 | 237 | if (bootstrap_register( 238 | bootstrapPort, "xyz.dmcyk.alice.as-a-service", recvPort) != 239 | KERN_SUCCESS) { 240 | return EXIT_FAILURE; 241 | } 242 | 243 | void *oolBuffer = NULL; 244 | vm_size_t oolBufferSize = vm_page_size; 245 | if (vm_allocate( 246 | mach_task_self(), 247 | (vm_address_t *)&oolBuffer, 248 | oolBufferSize, 249 | VM_PROT_READ | VM_PROT_WRITE) != KERN_SUCCESS) { 250 | printf("Failed to allocate memory buffer\n"); 251 | return 1; 252 | } 253 | 254 | strcpy((char *)oolBuffer, "Hello, OOL data!"); 255 | 256 | while (true) { 257 | // Message buffer. 258 | ReceiveMessage receiveMessage = {0}; 259 | 260 | mach_msg_return_t ret = receive_msg(recvPort, &receiveMessage); 261 | if (ret != MACH_MSG_SUCCESS) { 262 | printf("Failed to receive a message: %#x\n", ret); 263 | continue; 264 | } 265 | 266 | // Continue if there's no reply port. 267 | if (receiveMessage.message.header.msgh_remote_port == MACH_PORT_NULL) { 268 | continue; 269 | } 270 | 271 | switch (receiveMessage.message.header.msgh_id) { 272 | case MSG_ID_COPY_MEM: 273 | // As per request, respond with OOL memory. 274 | ret = send_ool_reply( 275 | receiveMessage.message.header.msgh_remote_port, 276 | &receiveMessage.message, 277 | oolBuffer, 278 | oolBufferSize, 279 | MACH_MSG_VIRTUAL_COPY, 280 | /* deallocate */ false); 281 | break; 282 | 283 | case MSG_ID_INSPECT_MEM: 284 | // Send several messages to inspect the behaviour of virtual memory for 285 | // different OOL options. 286 | ret = send_memory_inspection_messages(&receiveMessage); 287 | break; 288 | 289 | default: 290 | // Send a generic, inline response. 291 | ret = send_reply( 292 | receiveMessage.message.header.msgh_remote_port, 293 | &receiveMessage.message); 294 | break; 295 | } 296 | 297 | if (ret != MACH_MSG_SUCCESS) { 298 | printf("Failed to respond: %#x\n", ret); 299 | } 300 | } 301 | 302 | return 0; 303 | } 304 | -------------------------------------------------------------------------------- /04_mach_ool_vm/ool_data_transfer_options/bob_client.c: -------------------------------------------------------------------------------- 1 | #include "../vm_utils.h" 2 | #include "message.h" 3 | 4 | // Darwin 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | // std 17 | #include 18 | #include 19 | 20 | #define MS_IN_S 1000 21 | 22 | mach_msg_return_t 23 | receive_msg(mach_port_name_t recvPort, mach_msg_timeout_t timeout) { 24 | // Message buffer. 25 | ReceiveMessage receiveMessage = {0}; 26 | 27 | mach_msg_return_t ret = mach_msg( 28 | /* msg */ (mach_msg_header_t *)&receiveMessage, 29 | /* option */ MACH_RCV_MSG | MACH_RCV_TIMEOUT, 30 | /* send size */ 0, 31 | /* recv size */ sizeof(receiveMessage), 32 | /* recv_name */ recvPort, 33 | /* timeout */ timeout, 34 | /* notify port */ MACH_PORT_NULL); 35 | if (ret != MACH_MSG_SUCCESS) { 36 | return ret; 37 | } 38 | 39 | if (receiveMessage.message.header.msgh_id != MSG_ID_DEFAULT) { 40 | return RCV_ERROR_INVALID_MESSAGE_ID; 41 | } 42 | 43 | Message *message = &receiveMessage.message; 44 | 45 | printf("got response message!\n"); 46 | printf(" id : %d\n", message->header.msgh_id); 47 | printf(" bodyS : %s\n", message->bodyStr); 48 | printf(" bodyI : %d\n", message->bodyInt); 49 | 50 | return MACH_MSG_SUCCESS; 51 | } 52 | 53 | mach_msg_return_t 54 | receive_ool_message(mach_port_name_t recvPort, OOLReceiveMessage *rcvMessage) { 55 | mach_msg_return_t ret = mach_msg( 56 | /* msg */ (mach_msg_header_t *)rcvMessage, 57 | /* option */ MACH_RCV_MSG, 58 | /* send size */ 0, 59 | /* recv size */ sizeof(*rcvMessage), 60 | /* recv_name */ recvPort, 61 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 62 | /* notify port */ MACH_PORT_NULL); 63 | if (ret != MACH_MSG_SUCCESS) { 64 | return ret; 65 | } 66 | 67 | if (rcvMessage->message.header.msgh_id != MSG_ID_COPY_MEM && 68 | rcvMessage->message.header.msgh_id != MSG_ID_INSPECT_MEM) { 69 | return RCV_ERROR_INVALID_MESSAGE_ID; 70 | } 71 | 72 | return MACH_MSG_SUCCESS; 73 | } 74 | 75 | int main() { 76 | mach_port_name_t task = mach_task_self(); 77 | 78 | mach_port_t bootstrapPort; 79 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 80 | KERN_SUCCESS) { 81 | return EXIT_FAILURE; 82 | } 83 | 84 | mach_port_t port; 85 | if (bootstrap_look_up(bootstrapPort, "xyz.dmcyk.alice.as-a-service", &port) != 86 | KERN_SUCCESS) { 87 | return EXIT_FAILURE; 88 | } 89 | 90 | mach_port_t replyPort; 91 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &replyPort) != 92 | KERN_SUCCESS) { 93 | return EXIT_FAILURE; 94 | } 95 | 96 | if (mach_port_insert_right( 97 | task, replyPort, replyPort, MACH_MSG_TYPE_MAKE_SEND) != 98 | KERN_SUCCESS) { 99 | return EXIT_FAILURE; 100 | } 101 | 102 | Message message = {0}; 103 | message.header.msgh_remote_port = port; 104 | message.header.msgh_local_port = replyPort; 105 | 106 | // Setup message rights. 107 | message.header.msgh_bits = MACH_MSGH_BITS_SET( 108 | /* remote */ MACH_MSG_TYPE_COPY_SEND, 109 | /* local */ MACH_MSG_TYPE_MAKE_SEND, 110 | /* voucher */ 0, 111 | /* other */ 0); 112 | message.header.msgh_id = MSG_ID_DEFAULT; 113 | message.header.msgh_size = sizeof(message); 114 | 115 | strcpy(message.bodyStr, "Hello Mach!"); 116 | message.bodyInt = 0xff; 117 | 118 | mach_msg_return_t ret = mach_msg( 119 | /* msg */ (mach_msg_header_t *)&message, 120 | /* option */ MACH_SEND_MSG, 121 | /* send size */ sizeof(message), 122 | /* recv size */ 0, 123 | /* recv_name */ MACH_PORT_NULL, 124 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 125 | /* notify port */ MACH_PORT_NULL); 126 | 127 | if (ret != MACH_MSG_SUCCESS) { 128 | printf("Failed mach_msg: %d\n", ret); 129 | return EXIT_FAILURE; 130 | } 131 | 132 | while (ret == MACH_MSG_SUCCESS) { 133 | ret = receive_msg(replyPort, /* timeout */ 0.5 * MS_IN_S); 134 | } 135 | 136 | if (ret == MACH_RCV_TIMED_OUT) { 137 | // Just a timeout, continue. 138 | } else if (ret != MACH_MSG_SUCCESS) { 139 | printf("Failed to receive a message: %#x\n", ret); 140 | return 1; 141 | } 142 | 143 | strcpy(message.bodyStr, "Request OOL"); 144 | message.header.msgh_id = MSG_ID_INSPECT_MEM; 145 | ret = mach_msg( 146 | /* msg */ (mach_msg_header_t *)&message, 147 | /* option */ MACH_SEND_MSG, 148 | /* send size */ sizeof(message), 149 | /* recv size */ 0, 150 | /* recv_name */ MACH_PORT_NULL, 151 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 152 | /* notify port */ MACH_PORT_NULL); 153 | if (ret != MACH_MSG_SUCCESS) { 154 | printf("Failed mach_msg: %d\n", ret); 155 | return EXIT_FAILURE; 156 | } 157 | 158 | while (true) { 159 | OOLReceiveMessage rcvMessage = {0}; 160 | ret = receive_ool_message(replyPort, &rcvMessage); 161 | if (ret != MACH_MSG_SUCCESS) { 162 | printf("Failed to receive an OOL message: %#x\n", ret); 163 | return 1; 164 | } 165 | 166 | printf( 167 | "%s\n" 168 | " buffer addr: %p\n" 169 | " total buffer size: %#x\n" 170 | " copy option: %s\n", 171 | (const char *)rcvMessage.message.descriptor.address, 172 | rcvMessage.message.descriptor.address, 173 | rcvMessage.message.descriptor.size, 174 | mach_copy_option_to_str(rcvMessage.message.descriptor.copy)); 175 | 176 | if (rcvMessage.message.header.msgh_id == MSG_ID_INSPECT_MEM) { 177 | vm_region_dump_submap_info(rcvMessage.message.descriptor.address); 178 | printf("\n"); 179 | } 180 | } 181 | 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /04_mach_ool_vm/ool_data_transfer_options/message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | mach_msg_header_t header; 7 | char bodyStr[32]; 8 | int bodyInt; 9 | } Message; 10 | 11 | typedef struct { 12 | Message message; 13 | 14 | // Suitable for use with the default trailer type - no custom trailer 15 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 16 | // `MACH_RCV_TRAILER_NULL` type. 17 | mach_msg_trailer_t trailer; 18 | } ReceiveMessage; 19 | 20 | typedef struct { 21 | mach_msg_header_t header; 22 | mach_msg_size_t msgh_descriptor_count; 23 | mach_msg_ool_descriptor_t descriptor; 24 | } OOLMessage; 25 | 26 | typedef struct { 27 | OOLMessage message; 28 | 29 | // Suitable for use with the default trailer type - no custom trailer 30 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 31 | // `MACH_RCV_TRAILER_NULL` type. 32 | mach_msg_trailer_t trailer; 33 | } OOLReceiveMessage; 34 | 35 | #define MSG_ID_DEFAULT 8 36 | #define MSG_ID_COPY_MEM 10 37 | #define MSG_ID_INSPECT_MEM 11 38 | 39 | #define RCV_ERROR_INVALID_MESSAGE_ID 0xffffff01 40 | -------------------------------------------------------------------------------- /04_mach_ool_vm/ool_virtual_copy_large/alice_server.c: -------------------------------------------------------------------------------- 1 | #include "../vm_utils.h" 2 | #include "message.h" 3 | 4 | // Darwin 5 | #include 6 | #include 7 | #include 8 | 9 | // std 10 | #include 11 | #include 12 | #include 13 | 14 | mach_msg_return_t send_ool_reply( 15 | mach_port_name_t port, 16 | void *addr, 17 | mach_msg_size_t size, 18 | boolean_t fillAll, 19 | mach_msg_copy_options_t copy, 20 | boolean_t deallocate) { 21 | OOLMessage message = {0}; 22 | message.header.msgh_bits = MACH_MSGH_BITS_SET( 23 | /* remote */ MACH_MSG_TYPE_COPY_SEND, 24 | /* local */ 0, 25 | /* voucher */ 0, 26 | /* other */ MACH_MSGH_BITS_COMPLEX); 27 | message.header.msgh_remote_port = port; 28 | message.header.msgh_id = MSG_ID_INSPECT_MEM; 29 | message.header.msgh_size = sizeof(message); 30 | message.msgh_descriptor_count = 1; 31 | 32 | message.descriptor.address = addr; 33 | message.descriptor.size = size; 34 | message.descriptor.copy = copy; 35 | message.descriptor.deallocate = deallocate; 36 | message.descriptor.type = MACH_MSG_OOL_DESCRIPTOR; 37 | 38 | message.fill_all = fillAll; 39 | 40 | return mach_msg( 41 | /* msg */ (mach_msg_header_t *)&message, 42 | /* option */ MACH_SEND_MSG, 43 | /* send size */ sizeof(message), 44 | /* recv size */ 0, 45 | /* recv_name */ MACH_PORT_NULL, 46 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 47 | /* notify port */ MACH_PORT_NULL); 48 | } 49 | 50 | mach_msg_return_t 51 | receive_msg(mach_port_name_t recvPort, ReceiveMessage *buffer) { 52 | return mach_msg( 53 | /* msg */ (mach_msg_header_t *)buffer, 54 | /* option */ MACH_RCV_MSG, 55 | /* send size */ 0, 56 | /* recv size */ sizeof(*buffer), 57 | /* recv_name */ recvPort, 58 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 59 | /* notify port */ MACH_PORT_NULL); 60 | } 61 | 62 | void fill_pages(void *start, void *end, const char *data) { 63 | while (start < end) { 64 | strcpy((char *)start, data); 65 | 66 | start = (void *)((vm_address_t)start + vm_page_size); 67 | uint32_t *pageEnd = (uint32_t *)start; 68 | *(pageEnd - 1) = arc4random(); 69 | } 70 | } 71 | 72 | kern_return_t 73 | send_memory_inspection_messages(const ReceiveMessage *receiveMessage) { 74 | typedef struct { 75 | const char *name; 76 | vm_size_t size; 77 | mach_msg_copy_options_t copy; 78 | boolean_t deallocate; 79 | boolean_t fill_all; 80 | boolean_t fill_all_client; 81 | } MemoryOptions; 82 | 83 | MemoryOptions testCases[] = { 84 | {.name = "VIRTUAL;2GB;NO_FREE", 85 | .size = /* 2GB */ 2147483648, 86 | .copy = MACH_MSG_VIRTUAL_COPY, 87 | .deallocate = false, 88 | .fill_all = true, 89 | .fill_all_client = true}, 90 | {.name = "VIRTUAL;2GB;NO_FREE", 91 | .size = /* 2GB */ 2147483648, 92 | .copy = MACH_MSG_VIRTUAL_COPY, 93 | .deallocate = false, 94 | .fill_all = true, 95 | .fill_all_client = false}, 96 | }; 97 | 98 | for (int i = 0; i < sizeof(testCases) / sizeof(testCases[0]); ++i) { 99 | MemoryOptions testCase = testCases[i]; 100 | 101 | void *oolBuffer = NULL; 102 | mach_vm_size_t oolBufferSize = testCase.size; 103 | kern_return_t ret = mach_vm_allocate( 104 | mach_task_self(), 105 | (mach_vm_address_t *)&oolBuffer, 106 | oolBufferSize, 107 | VM_PROT_READ | VM_PROT_WRITE); 108 | if (ret != KERN_SUCCESS) { 109 | printf("Failed to allocate memory buffer: %x\n", ret); 110 | return KERN_FAILURE; 111 | } 112 | 113 | void *oolBufferEnd = (void *)((vm_address_t)oolBuffer + testCase.size); 114 | 115 | // Copy test case name. 116 | strcpy((char *)oolBuffer, testCase.name); 117 | 118 | if (testCase.fill_all) { 119 | fill_pages(oolBuffer, oolBufferEnd, testCase.name); 120 | } 121 | 122 | ret = send_ool_reply( 123 | receiveMessage->message.header.msgh_remote_port, 124 | oolBuffer, 125 | oolBufferSize, 126 | testCase.fill_all_client, 127 | testCase.copy, 128 | testCase.deallocate); 129 | printf("sent test case message: #%d, res - %d\n", i + 1, ret); 130 | 131 | // Stop if deallocated, the address is no longer valid here. 132 | if (testCase.deallocate) { 133 | continue; 134 | } 135 | 136 | vm_region_dump_submap_info(oolBuffer); 137 | 138 | if (testCase.fill_all) { 139 | fill_pages(oolBuffer, oolBufferEnd, "some other data\n"); 140 | } 141 | } 142 | 143 | return KERN_SUCCESS; 144 | } 145 | 146 | int main() { 147 | mach_port_t task = mach_task_self(); 148 | 149 | mach_port_name_t recvPort; 150 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &recvPort) != 151 | KERN_SUCCESS) { 152 | return EXIT_FAILURE; 153 | } 154 | 155 | if (mach_port_insert_right( 156 | task, recvPort, recvPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { 157 | return EXIT_FAILURE; 158 | } 159 | 160 | mach_port_t bootstrapPort; 161 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 162 | KERN_SUCCESS) { 163 | return EXIT_FAILURE; 164 | } 165 | 166 | if (bootstrap_register( 167 | bootstrapPort, "xyz.dmcyk.alice.as-a-service", recvPort) != 168 | KERN_SUCCESS) { 169 | return EXIT_FAILURE; 170 | } 171 | 172 | while (true) { 173 | // Message buffer. 174 | ReceiveMessage receiveMessage = {0}; 175 | 176 | mach_msg_return_t ret = receive_msg(recvPort, &receiveMessage); 177 | if (ret != MACH_MSG_SUCCESS) { 178 | printf("Failed to receive a message: %#x\n", ret); 179 | continue; 180 | } 181 | 182 | // Continue if there's no reply port. 183 | if (receiveMessage.message.header.msgh_remote_port == MACH_PORT_NULL) { 184 | continue; 185 | } 186 | 187 | switch (receiveMessage.message.header.msgh_id) { 188 | case MSG_ID_INSPECT_MEM: 189 | // Send several messages to inspect the behaviour of virtual memory for 190 | // different OOL options. 191 | ret = send_memory_inspection_messages(&receiveMessage); 192 | break; 193 | 194 | default: 195 | printf("Got message with unhandled id\n"); 196 | break; 197 | } 198 | 199 | if (ret != MACH_MSG_SUCCESS) { 200 | printf("Failed to respond: %#x\n", ret); 201 | } 202 | } 203 | 204 | return 0; 205 | } 206 | -------------------------------------------------------------------------------- /04_mach_ool_vm/ool_virtual_copy_large/bob_client.c: -------------------------------------------------------------------------------- 1 | #include "../vm_utils.h" 2 | #include "message.h" 3 | 4 | // Darwin 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | // std 17 | #include 18 | #include 19 | 20 | #define MS_IN_S 1000 21 | 22 | mach_msg_return_t 23 | receive_ool_message(mach_port_name_t recvPort, OOLReceiveMessage *rcvMessage) { 24 | mach_msg_return_t ret = mach_msg( 25 | /* msg */ (mach_msg_header_t *)rcvMessage, 26 | /* option */ MACH_RCV_MSG, 27 | /* send size */ 0, 28 | /* recv size */ sizeof(*rcvMessage), 29 | /* recv_name */ recvPort, 30 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 31 | /* notify port */ MACH_PORT_NULL); 32 | if (ret != MACH_MSG_SUCCESS) { 33 | return ret; 34 | } 35 | 36 | if (rcvMessage->message.header.msgh_id != MSG_ID_COPY_MEM && 37 | rcvMessage->message.header.msgh_id != MSG_ID_INSPECT_MEM) { 38 | return RCV_ERROR_INVALID_MESSAGE_ID; 39 | } 40 | 41 | return MACH_MSG_SUCCESS; 42 | } 43 | 44 | void fill_pages(void *start, void *end, const char *data) { 45 | while (start < end) { 46 | strcpy((char *)start, data); 47 | 48 | start = (void *)((vm_address_t)start + vm_page_size); 49 | uint32_t *pageEnd = (uint32_t *)start; 50 | *(pageEnd - 1) = arc4random(); 51 | } 52 | } 53 | 54 | int main() { 55 | mach_port_name_t task = mach_task_self(); 56 | 57 | mach_port_t bootstrapPort; 58 | if (task_get_special_port(task, TASK_BOOTSTRAP_PORT, &bootstrapPort) != 59 | KERN_SUCCESS) { 60 | return EXIT_FAILURE; 61 | } 62 | 63 | mach_port_t port; 64 | if (bootstrap_look_up(bootstrapPort, "xyz.dmcyk.alice.as-a-service", &port) != 65 | KERN_SUCCESS) { 66 | return EXIT_FAILURE; 67 | } 68 | 69 | mach_port_t replyPort; 70 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &replyPort) != 71 | KERN_SUCCESS) { 72 | return EXIT_FAILURE; 73 | } 74 | 75 | if (mach_port_insert_right( 76 | task, replyPort, replyPort, MACH_MSG_TYPE_MAKE_SEND) != 77 | KERN_SUCCESS) { 78 | return EXIT_FAILURE; 79 | } 80 | 81 | Message message = {0}; 82 | message.header.msgh_remote_port = port; 83 | message.header.msgh_local_port = replyPort; 84 | 85 | // Setup message rights. 86 | message.header.msgh_bits = MACH_MSGH_BITS_SET( 87 | /* remote */ MACH_MSG_TYPE_COPY_SEND, 88 | /* local */ MACH_MSG_TYPE_MAKE_SEND, 89 | /* voucher */ 0, 90 | /* other */ 0); 91 | message.header.msgh_id = MSG_ID_INSPECT_MEM; 92 | message.header.msgh_size = sizeof(message); 93 | 94 | kern_return_t ret = mach_msg( 95 | /* msg */ (mach_msg_header_t *)&message, 96 | /* option */ MACH_SEND_MSG, 97 | /* send size */ sizeof(message), 98 | /* recv size */ 0, 99 | /* recv_name */ MACH_PORT_NULL, 100 | /* timeout */ MACH_MSG_TIMEOUT_NONE, 101 | /* notify port */ MACH_PORT_NULL); 102 | if (ret != MACH_MSG_SUCCESS) { 103 | printf("Failed mach_msg: %d\n", ret); 104 | return EXIT_FAILURE; 105 | } 106 | 107 | while (true) { 108 | OOLReceiveMessage rcvMessage = {0}; 109 | ret = receive_ool_message(replyPort, &rcvMessage); 110 | if (ret != MACH_MSG_SUCCESS) { 111 | printf("Failed to receive an OOL message: %#x\n", ret); 112 | return 1; 113 | } 114 | 115 | printf( 116 | "%s\n" 117 | " buffer addr: %p\n" 118 | " total buffer size: %x\n" 119 | " copy option: %s\n", 120 | (const char *)rcvMessage.message.descriptor.address, 121 | rcvMessage.message.descriptor.address, 122 | rcvMessage.message.descriptor.size, 123 | mach_copy_option_to_str(rcvMessage.message.descriptor.copy)); 124 | 125 | if (rcvMessage.message.header.msgh_id == MSG_ID_INSPECT_MEM) { 126 | vm_region_dump_submap_info(rcvMessage.message.descriptor.address); 127 | printf("\n"); 128 | 129 | if (rcvMessage.message.fill_all) { 130 | void *start = rcvMessage.message.descriptor.address; 131 | void *end = 132 | (void *)((vm_address_t)start + rcvMessage.message.descriptor.size); 133 | fill_pages(start, end, "foo"); 134 | } 135 | } 136 | } 137 | 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /04_mach_ool_vm/ool_virtual_copy_large/message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | mach_msg_header_t header; 7 | } Message; 8 | 9 | typedef struct { 10 | Message message; 11 | 12 | // Suitable for use with the default trailer type - no custom trailer 13 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 14 | // `MACH_RCV_TRAILER_NULL` type. 15 | mach_msg_trailer_t trailer; 16 | } ReceiveMessage; 17 | 18 | typedef struct { 19 | mach_msg_header_t header; 20 | mach_msg_size_t msgh_descriptor_count; 21 | mach_msg_ool_descriptor_t descriptor; 22 | 23 | boolean_t fill_all; 24 | } OOLMessage; 25 | 26 | typedef struct { 27 | OOLMessage message; 28 | 29 | // Suitable for use with the default trailer type - no custom trailer 30 | // information requested using `MACH_RCV_TRAILER_TYPE`, or just the explicit 31 | // `MACH_RCV_TRAILER_NULL` type. 32 | mach_msg_trailer_t trailer; 33 | } OOLReceiveMessage; 34 | 35 | #define MSG_ID_DEFAULT 8 36 | #define MSG_ID_COPY_MEM 10 37 | #define MSG_ID_INSPECT_MEM 11 38 | 39 | #define RCV_ERROR_INVALID_MESSAGE_ID 0xffffff01 40 | -------------------------------------------------------------------------------- /04_mach_ool_vm/vm_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Darwin 4 | #include 5 | #include 6 | #include 7 | 8 | // std 9 | #include 10 | #include 11 | 12 | static const char *vm_share_mode_to_str(unsigned char share_mode) { 13 | switch (share_mode) { 14 | case SM_COW: 15 | return "SM_COW"; 16 | case SM_PRIVATE: 17 | return "SM_PRIVATE"; 18 | case SM_EMPTY: 19 | return "SM_EMPTY"; 20 | case SM_SHARED: 21 | return "SM_SHARED"; 22 | case SM_TRUESHARED: 23 | return "SM_TRUESHARED"; 24 | case SM_PRIVATE_ALIASED: 25 | return "SM_PRIVATE_ALIASED"; 26 | case SM_SHARED_ALIASED: 27 | return "SM_SHARED_ALIASED"; 28 | case SM_LARGE_PAGE: 29 | return "SM_LARGE_PAGE"; 30 | default: 31 | return "unknown"; 32 | } 33 | } 34 | 35 | static const char *vm_prot_to_str(vm_prot_t prot) { 36 | if (prot == VM_PROT_ALL) { 37 | return "VM_PROT_ALL"; 38 | } 39 | 40 | if (prot == VM_PROT_DEFAULT) { 41 | return "VM_PROT_DEFAULT"; 42 | } 43 | 44 | if (prot == VM_PROT_EXECUTE_ONLY) { 45 | return "VM_PROT_EXECUTE_ONLY"; 46 | } 47 | 48 | char buffer[1024] = {0}; 49 | sprintf(buffer, "%#x", prot); 50 | 51 | // note, this will leak memory 52 | return strdup(buffer); 53 | } 54 | 55 | static const char *mach_copy_option_to_str(mach_msg_copy_options_t option) { 56 | switch (option) { 57 | case MACH_MSG_PHYSICAL_COPY: 58 | return "MACH_MSG_PHYSICAL_COPY"; 59 | case MACH_MSG_VIRTUAL_COPY: 60 | return "MACH_MSG_VIRTUAL_COPY"; 61 | default: 62 | return "unknown"; 63 | } 64 | } 65 | 66 | static kern_return_t vm_region_dump_basic_info(void *_addr) { 67 | mach_vm_address_t addr = (mach_vm_address_t)_addr; 68 | mach_vm_size_t size = vm_page_size; 69 | 70 | vm_region_basic_info_data_64_t info = {0}; 71 | vm_region_flavor_t flavour = VM_REGION_BASIC_INFO_64; 72 | mach_msg_type_number_t infoCnt = VM_REGION_BASIC_INFO_COUNT_64; 73 | mach_port_name_t object; 74 | 75 | kern_return_t res = mach_vm_region( 76 | mach_task_self(), 77 | &addr, 78 | &size, 79 | flavour, 80 | (vm_region_info_t)&info, 81 | &infoCnt, 82 | &object); 83 | if (res != KERN_SUCCESS) { 84 | return res; 85 | } 86 | 87 | printf( 88 | "region info: %p - %p (%#llx)\n" 89 | " prot: %s\n" 90 | " maxprot: %s\n" 91 | " inheritance: %u\n" 92 | " shared: %d\n" 93 | " reserved: %d\n" 94 | " offset: %llu\n" 95 | " behavior: %d\n", 96 | (void *)addr, 97 | (void *)(addr + size), 98 | size, 99 | vm_prot_to_str(info.protection), 100 | vm_prot_to_str(info.max_protection), 101 | info.inheritance, 102 | info.shared, 103 | info.reserved, 104 | info.offset, 105 | info.behavior); 106 | 107 | return KERN_SUCCESS; 108 | } 109 | 110 | static kern_return_t vm_region_dump_extended_info(void *_addr) { 111 | mach_vm_address_t addr = (mach_vm_address_t)_addr; 112 | mach_vm_size_t size = vm_page_size; 113 | 114 | vm_region_extended_info_data_t info = {0}; 115 | vm_region_flavor_t flavour = VM_REGION_EXTENDED_INFO; 116 | mach_msg_type_number_t infoCnt = VM_REGION_EXTENDED_INFO_COUNT; 117 | mach_port_name_t object; 118 | 119 | kern_return_t ret = mach_vm_region( 120 | mach_task_self(), 121 | &addr, 122 | &size, 123 | flavour, 124 | (vm_region_info_t)&info, 125 | &infoCnt, 126 | &object); 127 | if (ret != KERN_SUCCESS) { 128 | return ret; 129 | } 130 | 131 | printf( 132 | "region info: %p - %p (%#llx)\n" 133 | " prot: %s\n" 134 | " pages_resident: %u\n" 135 | " pages_shared_now_private: %u\n" 136 | " pages_swapped_out: %u\n" 137 | " pages_dirtied: %u\n" 138 | " ref_count: %u\n" 139 | " shadow_depth: %u\n" 140 | " external_pager: %u\n" 141 | " share_mode: %s\n" 142 | " pages_reusable: %u\n", 143 | (void *)addr, 144 | (void *)(addr + size), 145 | size, 146 | vm_prot_to_str(info.protection), 147 | info.pages_resident, 148 | info.pages_shared_now_private, 149 | info.pages_swapped_out, 150 | info.pages_dirtied, 151 | info.ref_count, 152 | (unsigned int)info.shadow_depth, 153 | (unsigned int)info.external_pager, 154 | vm_share_mode_to_str(info.share_mode), 155 | info.pages_reusable); 156 | 157 | return KERN_SUCCESS; 158 | } 159 | 160 | static kern_return_t vm_region_get_submap_info( 161 | mach_vm_address_t *addr, 162 | mach_vm_size_t *size, 163 | natural_t *nestingDepth, 164 | vm_region_submap_info_data_64_t *info) { 165 | mach_msg_type_number_t infoCnt = VM_REGION_SUBMAP_INFO_COUNT_64; 166 | 167 | return mach_vm_region_recurse( 168 | mach_task_self(), 169 | addr, 170 | size, 171 | nestingDepth, 172 | (vm_region_recurse_info_t)info, 173 | &infoCnt); 174 | } 175 | 176 | static kern_return_t vm_region_dump_submap_info(void *_addr) { 177 | mach_vm_address_t addr = (mach_vm_address_t)_addr; 178 | mach_vm_size_t size = 0; 179 | vm_region_submap_info_data_64_t info = {0}; 180 | 181 | // Use high nesting depth to get the most nested submap. 182 | natural_t nestingDepth = 99; 183 | 184 | kern_return_t ret = 185 | vm_region_get_submap_info(&addr, &size, &nestingDepth, &info); 186 | if (ret != KERN_SUCCESS) { 187 | return ret; 188 | } 189 | 190 | printf( 191 | "region info: %p - %p (%#llx)\n" 192 | " prot: %s\n" 193 | " maxprot: %s\n" 194 | " nestingDepth: %d\n" 195 | " inheritance: %u\n" 196 | " pages_resident: %u\n" 197 | " pages_shared_now_private: %u\n" 198 | " pages_swapped_out: %u\n" 199 | " pages_dirtied: %u\n" 200 | " ref_count: %u\n" 201 | " shadow_depth: %u\n" 202 | " external_pager: %u\n" 203 | " share_mode: %s\n" 204 | " is_submap: %d\n" 205 | " behavior: %d\n" 206 | " pages_reusable: %u\n", 207 | (void *)addr, 208 | (void *)(addr + size), 209 | size, 210 | vm_prot_to_str(info.protection), 211 | vm_prot_to_str(info.max_protection), 212 | nestingDepth, 213 | info.inheritance, 214 | info.pages_resident, 215 | info.pages_shared_now_private, 216 | info.pages_swapped_out, 217 | info.pages_dirtied, 218 | info.ref_count, 219 | (unsigned int)info.shadow_depth, 220 | (unsigned int)info.external_pager, 221 | vm_share_mode_to_str(info.share_mode), 222 | (int)info.is_submap, 223 | info.behavior, 224 | info.pages_reusable); 225 | 226 | return KERN_SUCCESS; 227 | } 228 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13 FATAL_ERROR) 2 | 3 | project(mach_ipc) 4 | 5 | # Ensure ninja shows color diagnostics 6 | add_compile_options(-fdiagnostics-color=always) 7 | 8 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 9 | 10 | # Example, always build in debug 11 | add_definitions(-O0 -g) 12 | 13 | add_subdirectory(01_mach_messages) 14 | add_subdirectory(02_mach_bidirectional) 15 | add_subdirectory(03_mach_ool_intro) 16 | add_subdirectory(04_mach_ool_vm) 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Damian Malarczyk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xnu_ipc 2 | 3 | Companion repository to a series of blog posts covering XNU IPC. 4 | 5 | ## TOC 6 | 7 | 1. [XNU IPC - Mach messages](https://dmcyk.xyz/post/xnu_ipc_i_mach_messages/) in 8 | `01_mach_messages` 9 | 2. [XNU IPC - bidirectional Mach messages](https://dmcyk.xyz/post/xnu_ipc_ii_message_apis/xnu_ipc_ii_message_apis/) 10 | in `02_mach_bidirectional` 11 | 3. [XNU IPC - Introduction to OOL data](https://dmcyk.xyz/post/xnu_ipc_iii_ool_data/) 12 | in `03_mach_ool_intro` 13 | 3. [XNU IPC - OOL data and virtual memory](https://dmcyk.xyz/post/xnu_ipc_iv_ool_vm/) 14 | in `04_mach_ool_vm` 15 | --------------------------------------------------------------------------------