• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "testing/libfuzzer/fuzzers/mach/mach_message_converter.h"
11 
12 #include <string.h>
13 #include <sys/types.h>
14 
15 #include <utility>
16 
17 #include "base/apple/mach_logging.h"
18 #include "base/containers/buffer_iterator.h"
19 #include "base/mac/scoped_mach_msg_destroy.h"
20 
21 namespace mach_fuzzer {
22 
23 namespace {
24 
ConvertPort(const MachPortType & port_proto)25 SendablePort ConvertPort(const MachPortType& port_proto) {
26   struct {
27     bool insert_send_right;
28     bool deallocate_receive_right;
29     mach_msg_type_name_t disposition;
30   } recipe;
31   switch (port_proto) {
32     case RECEIVE:
33       recipe = {true, false, MACH_MSG_TYPE_MOVE_RECEIVE};
34       break;
35     case SEND:
36       recipe = {false, false, MACH_MSG_TYPE_MAKE_SEND};
37       break;
38     case SEND_ONCE:
39       recipe = {false, false, MACH_MSG_TYPE_MAKE_SEND_ONCE};
40       break;
41     case DEAD_NAME:
42       recipe = {true, true, MACH_MSG_TYPE_COPY_SEND};
43       break;
44     case RECEIVE_NO_SENDERS:
45       recipe = {false, false, MACH_MSG_TYPE_MOVE_RECEIVE};
46       break;
47   }
48 
49   SendablePort port;
50   kern_return_t kr = mach_port_allocate(
51       mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
52       base::apple::ScopedMachReceiveRight::Receiver(port.receive_right).get());
53   MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_allocate";
54 
55   port.name = port.receive_right.get();
56   port.disposition = recipe.disposition;
57   port.proto_type = port_proto;
58 
59   if (recipe.insert_send_right) {
60     kr = mach_port_insert_right(mach_task_self(), port.name, port.name,
61                                 MACH_MSG_TYPE_MAKE_SEND);
62     MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_right";
63     port.send_right.reset(port.name);
64   }
65 
66   if (recipe.deallocate_receive_right) {
67     port.receive_right.reset();
68   }
69 
70   return port;
71 }
72 
ConvertDescriptor(base::BufferIterator<uint8_t> * iterator,const Descriptor & descriptor_proto,SendablePort * opt_port)73 bool ConvertDescriptor(base::BufferIterator<uint8_t>* iterator,
74                        const Descriptor& descriptor_proto,
75                        SendablePort* opt_port) {
76   switch (descriptor_proto.descriptor_oneof_case()) {
77     case Descriptor::kPort: {
78       auto* descriptor = iterator->MutableObject<mach_msg_port_descriptor_t>();
79       SendablePort port = ConvertPort(descriptor_proto.port());
80       descriptor->name = port.name;
81       descriptor->pad1 = 0;
82       descriptor->pad2 = 0;
83       descriptor->disposition = port.disposition;
84       descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
85       *opt_port = std::move(port);
86       return true;
87     }
88     case Descriptor::kOol: {
89       auto* descriptor = iterator->MutableObject<mach_msg_ool_descriptor_t>();
90       descriptor->address =
91           const_cast<char*>(descriptor_proto.ool().data().data());
92       descriptor->size = descriptor_proto.ool().data().size();
93       descriptor->copy = MACH_MSG_VIRTUAL_COPY;
94       descriptor->pad1 = 0;
95       descriptor->type = MACH_MSG_OOL_DESCRIPTOR;
96       return true;
97     }
98     default:
99       return false;
100   }
101 }
102 
103 }  // namespace
104 
ConvertProtoToMachMessage(const MachMessage & proto)105 SendableMessage ConvertProtoToMachMessage(const MachMessage& proto) {
106   SendableMessage message;
107 
108   const size_t descriptor_count = proto.descriptors().size();
109   const size_t data_size = proto.data().size();
110   const bool include_body =
111       proto.include_body_if_not_complex() || descriptor_count > 0;
112 
113   // This is the maximum size of the message. Depending on the descriptor type,
114   // the actual msgh_size may be less.
115   const size_t message_size =
116       sizeof(mach_msg_header_t) + (include_body ? sizeof(mach_msg_body_t) : 0) +
117       (sizeof(mach_msg_descriptor_t) * descriptor_count) + data_size;
118   message.buffer = std::make_unique<uint8_t[]>(round_msg(message_size));
119 
120   base::BufferIterator<uint8_t> iterator(message.buffer.get(), message_size);
121 
122   auto* header = iterator.MutableObject<mach_msg_header_t>();
123   message.header = header;
124   header->msgh_id = proto.id();
125 
126   if (proto.has_local_port()) {
127     SendablePort port = ConvertPort(proto.local_port());
128     auto disposition = port.disposition;
129     // It's not legal to have a receive reply report.
130     if (disposition != MACH_MSG_TYPE_MOVE_RECEIVE) {
131       header->msgh_bits |= MACH_MSGH_BITS(0, disposition);
132       header->msgh_local_port = port.name;
133       message.ports.push_back(std::move(port));
134     }
135   }
136 
137   if (include_body) {
138     auto* body = iterator.MutableObject<mach_msg_body_t>();
139     body->msgh_descriptor_count = descriptor_count;
140   }
141 
142   if (descriptor_count > 0) {
143     header->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
144     for (const auto& descriptor : proto.descriptors()) {
145       SendablePort opt_port;
146       if (!ConvertDescriptor(&iterator, descriptor, &opt_port)) {
147         return SendableMessage();
148       }
149       if (opt_port.name != MACH_PORT_NULL) {
150         message.ports.push_back(std::move(opt_port));
151       }
152     }
153   }
154 
155   auto data = iterator.MutableSpan<uint8_t>(data_size);
156   memcpy(data.data(), proto.data().data(), proto.data().size());
157 
158   header->msgh_size = round_msg(iterator.position());
159 
160   return message;
161 }
162 
SendMessage(mach_port_t remote_port,const MachMessage & proto)163 SendResult SendMessage(mach_port_t remote_port, const MachMessage& proto) {
164   SendResult result;
165   result.message = ConvertProtoToMachMessage(proto);
166   if (!result.message.header) {
167     result.kr = KERN_FAILURE;
168     return result;
169   }
170 
171   result.message.header->msgh_remote_port = remote_port;
172   result.message.header->msgh_bits |=
173       MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
174 
175   base::ScopedMachMsgDestroy scoped_message(result.message.header);
176 
177   result.kr = mach_msg(result.message.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
178                        result.message.header->msgh_size, 0, MACH_PORT_NULL,
179                        /*timeout=*/0, MACH_PORT_NULL);
180 
181   if (result.kr == KERN_SUCCESS) {
182     scoped_message.Disarm();
183   }
184 
185   return result;
186 }
187 
188 }  // namespace mach_fuzzer
189