blob: 9d734456d0ffdb5c07cda4ff291c2b4e77e2c4fa [file] [log] [blame]
Tom Cherrye275d6d2017-12-11 23:31:33 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <errno.h>
30#include <poll.h>
31#include <stdatomic.h>
32#include <stddef.h>
33#include <stdint.h>
34#include <stdlib.h>
35#include <string.h>
36#include <sys/socket.h>
Elliott Hughes620eec12024-08-07 17:59:53 +000037#include <sys/system_properties.h>
Tom Cherrye275d6d2017-12-11 23:31:33 -080038#include <sys/types.h>
39#include <sys/uio.h>
40#include <sys/un.h>
Tom Cherrye275d6d2017-12-11 23:31:33 -080041#include <unistd.h>
42
Chienyuan Huangf2b4a972020-08-20 08:42:31 +000043#include <async_safe/log.h>
Bowgo Tsai61a5a832021-07-15 10:13:33 +000044#include <async_safe/CHECK.h>
Tom Cherrye275d6d2017-12-11 23:31:33 -080045
Bowgo Tsai61a5a832021-07-15 10:13:33 +000046#include "private/bionic_defs.h"
Josh Gao4956c372019-12-19 16:35:51 -080047#include "platform/bionic/macros.h"
Elliott Hughes6cb70ad2019-10-31 15:37:32 -070048#include "private/ScopedFd.h"
Tom Cherrye275d6d2017-12-11 23:31:33 -080049
50static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
Paul Lawrence71ade012023-07-18 08:49:44 -070051static const char property_service_for_system_socket[] =
52 "/dev/socket/" PROP_SERVICE_FOR_SYSTEM_NAME;
Tom Cherrye275d6d2017-12-11 23:31:33 -080053static const char* kServiceVersionPropertyName = "ro.property_service.version";
54
55class PropertyServiceConnection {
56 public:
Paul Lawrence71ade012023-07-18 08:49:44 -070057 PropertyServiceConnection(const char* name) : last_error_(0) {
Elliott Hughes6cb70ad2019-10-31 15:37:32 -070058 socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));
59 if (socket_.get() == -1) {
Tom Cherrye275d6d2017-12-11 23:31:33 -080060 last_error_ = errno;
61 return;
62 }
63
Paul Lawrence71ade012023-07-18 08:49:44 -070064 // If we're trying to set "sys.powerctl" from a privileged process, use the special
65 // socket. Because this socket is only accessible to privileged processes, it can't
66 // be DoSed directly by malicious apps. (The shell user should be able to reboot,
67 // though, so we don't just always use the special socket for "sys.powerctl".)
68 // See b/262237198 for context
69 const char* socket = property_service_socket;
70 if (strcmp(name, "sys.powerctl") == 0 &&
71 access(property_service_for_system_socket, W_OK) == 0) {
72 socket = property_service_for_system_socket;
73 }
74
75 const size_t namelen = strlen(socket);
Tom Cherrye275d6d2017-12-11 23:31:33 -080076 sockaddr_un addr;
77 memset(&addr, 0, sizeof(addr));
Paul Lawrence71ade012023-07-18 08:49:44 -070078 strlcpy(addr.sun_path, socket, sizeof(addr.sun_path));
Tom Cherrye275d6d2017-12-11 23:31:33 -080079 addr.sun_family = AF_LOCAL;
80 socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
81
Elliott Hughes6cb70ad2019-10-31 15:37:32 -070082 if (TEMP_FAILURE_RETRY(connect(socket_.get(),
83 reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
Tom Cherrye275d6d2017-12-11 23:31:33 -080084 last_error_ = errno;
Elliott Hughes6cb70ad2019-10-31 15:37:32 -070085 socket_.reset();
Tom Cherrye275d6d2017-12-11 23:31:33 -080086 }
87 }
88
89 bool IsValid() {
Elliott Hughes6cb70ad2019-10-31 15:37:32 -070090 return socket_.get() != -1;
Tom Cherrye275d6d2017-12-11 23:31:33 -080091 }
92
93 int GetLastError() {
94 return last_error_;
95 }
96
97 bool RecvInt32(int32_t* value) {
Elliott Hughes6cb70ad2019-10-31 15:37:32 -070098 int result = TEMP_FAILURE_RETRY(recv(socket_.get(), value, sizeof(*value), MSG_WAITALL));
Tom Cherrye275d6d2017-12-11 23:31:33 -080099 return CheckSendRecvResult(result, sizeof(*value));
100 }
101
102 int socket() {
Elliott Hughes6cb70ad2019-10-31 15:37:32 -0700103 return socket_.get();
Tom Cherrye275d6d2017-12-11 23:31:33 -0800104 }
105
106 private:
107 bool CheckSendRecvResult(int result, int expected_len) {
108 if (result == -1) {
109 last_error_ = errno;
110 } else if (result != expected_len) {
111 last_error_ = -1;
112 } else {
113 last_error_ = 0;
114 }
115
116 return last_error_ == 0;
117 }
118
Elliott Hughes6cb70ad2019-10-31 15:37:32 -0700119 ScopedFd socket_;
Tom Cherrye275d6d2017-12-11 23:31:33 -0800120 int last_error_;
121
122 friend class SocketWriter;
123};
124
125class SocketWriter {
126 public:
127 explicit SocketWriter(PropertyServiceConnection* connection)
128 : connection_(connection), iov_index_(0), uint_buf_index_(0) {
129 }
130
131 SocketWriter& WriteUint32(uint32_t value) {
132 CHECK(uint_buf_index_ < kUintBufSize);
133 CHECK(iov_index_ < kIovSize);
134 uint32_t* ptr = uint_buf_ + uint_buf_index_;
135 uint_buf_[uint_buf_index_++] = value;
136 iov_[iov_index_].iov_base = ptr;
137 iov_[iov_index_].iov_len = sizeof(*ptr);
138 ++iov_index_;
139 return *this;
140 }
141
142 SocketWriter& WriteString(const char* value) {
143 uint32_t valuelen = strlen(value);
144 WriteUint32(valuelen);
145 if (valuelen == 0) {
146 return *this;
147 }
148
149 CHECK(iov_index_ < kIovSize);
150 iov_[iov_index_].iov_base = const_cast<char*>(value);
151 iov_[iov_index_].iov_len = valuelen;
152 ++iov_index_;
153
154 return *this;
155 }
156
157 bool Send() {
158 if (!connection_->IsValid()) {
159 return false;
160 }
161
162 if (writev(connection_->socket(), iov_, iov_index_) == -1) {
163 connection_->last_error_ = errno;
164 return false;
165 }
166
167 iov_index_ = uint_buf_index_ = 0;
168 return true;
169 }
170
171 private:
172 static constexpr size_t kUintBufSize = 8;
173 static constexpr size_t kIovSize = 8;
174
175 PropertyServiceConnection* connection_;
176 iovec iov_[kIovSize];
177 size_t iov_index_;
178 uint32_t uint_buf_[kUintBufSize];
179 size_t uint_buf_index_;
180
Elliott Hughes5e62b342018-10-25 11:00:00 -0700181 BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(SocketWriter);
Tom Cherrye275d6d2017-12-11 23:31:33 -0800182};
183
184struct prop_msg {
185 unsigned cmd;
186 char name[PROP_NAME_MAX];
187 char value[PROP_VALUE_MAX];
188};
189
190static int send_prop_msg(const prop_msg* msg) {
Paul Lawrence71ade012023-07-18 08:49:44 -0700191 PropertyServiceConnection connection(msg->name);
Tom Cherrye275d6d2017-12-11 23:31:33 -0800192 if (!connection.IsValid()) {
193 return connection.GetLastError();
194 }
195
196 int result = -1;
197 int s = connection.socket();
198
199 const int num_bytes = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));
200 if (num_bytes == sizeof(prop_msg)) {
201 // We successfully wrote to the property server but now we
202 // wait for the property server to finish its work. It
203 // acknowledges its completion by closing the socket so we
204 // poll here (on nothing), waiting for the socket to close.
205 // If you 'adb shell setprop foo bar' you'll see the POLLHUP
206 // once the socket closes. Out of paranoia we cap our poll
207 // at 250 ms.
208 pollfd pollfds[1];
209 pollfds[0].fd = s;
210 pollfds[0].events = 0;
211 const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));
212 if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) {
213 result = 0;
214 } else {
215 // Ignore the timeout and treat it like a success anyway.
216 // The init process is single-threaded and its property
217 // service is sometimes slow to respond (perhaps it's off
218 // starting a child process or something) and thus this
219 // times out and the caller thinks it failed, even though
220 // it's still getting around to it. So we fake it here,
221 // mostly for ctl.* properties, but we do try and wait 250
222 // ms so callers who do read-after-write can reliably see
223 // what they've written. Most of the time.
Tom Cherrye275d6d2017-12-11 23:31:33 -0800224 async_safe_format_log(ANDROID_LOG_WARN, "libc",
225 "Property service has timed out while trying to set \"%s\" to \"%s\"",
226 msg->name, msg->value);
227 result = 0;
228 }
229 }
230
231 return result;
232}
233
234static constexpr uint32_t kProtocolVersion1 = 1;
235static constexpr uint32_t kProtocolVersion2 = 2; // current
236
237static atomic_uint_least32_t g_propservice_protocol_version = 0;
238
239static void detect_protocol_version() {
240 char value[PROP_VALUE_MAX];
241 if (__system_property_get(kServiceVersionPropertyName, value) == 0) {
242 g_propservice_protocol_version = kProtocolVersion1;
243 async_safe_format_log(ANDROID_LOG_WARN, "libc",
244 "Using old property service protocol (\"%s\" is not set)",
245 kServiceVersionPropertyName);
246 } else {
247 uint32_t version = static_cast<uint32_t>(atoll(value));
248 if (version >= kProtocolVersion2) {
249 g_propservice_protocol_version = kProtocolVersion2;
250 } else {
251 async_safe_format_log(ANDROID_LOG_WARN, "libc",
252 "Using old property service protocol (\"%s\"=\"%s\")",
253 kServiceVersionPropertyName, value);
254 g_propservice_protocol_version = kProtocolVersion1;
255 }
256 }
257}
258
Elliott Hughesa9335822024-04-22 21:01:38 +0000259static const char* __prop_error_to_string(int error) {
260 switch (error) {
261 case PROP_ERROR_READ_CMD: return "PROP_ERROR_READ_CMD";
262 case PROP_ERROR_READ_DATA: return "PROP_ERROR_READ_DATA";
263 case PROP_ERROR_READ_ONLY_PROPERTY: return "PROP_ERROR_READ_ONLY_PROPERTY";
264 case PROP_ERROR_INVALID_NAME: return "PROP_ERROR_INVALID_NAME";
265 case PROP_ERROR_INVALID_VALUE: return "PROP_ERROR_INVALID_VALUE";
266 case PROP_ERROR_PERMISSION_DENIED: return "PROP_ERROR_PERMISSION_DENIED";
267 case PROP_ERROR_INVALID_CMD: return "PROP_ERROR_INVALID_CMD";
268 case PROP_ERROR_HANDLE_CONTROL_MESSAGE: return "PROP_ERROR_HANDLE_CONTROL_MESSAGE";
269 case PROP_ERROR_SET_FAILED: return "PROP_ERROR_SET_FAILED";
270 }
271 return "<unknown>";
272}
273
Tom Cherrye275d6d2017-12-11 23:31:33 -0800274__BIONIC_WEAK_FOR_NATIVE_BRIDGE
275int __system_property_set(const char* key, const char* value) {
276 if (key == nullptr) return -1;
277 if (value == nullptr) value = "";
278
Tom Cherrye275d6d2017-12-11 23:31:33 -0800279 if (g_propservice_protocol_version == 0) {
280 detect_protocol_version();
281 }
282
283 if (g_propservice_protocol_version == kProtocolVersion1) {
284 // Old protocol does not support long names or values
285 if (strlen(key) >= PROP_NAME_MAX) return -1;
286 if (strlen(value) >= PROP_VALUE_MAX) return -1;
287
288 prop_msg msg;
289 memset(&msg, 0, sizeof msg);
290 msg.cmd = PROP_MSG_SETPROP;
291 strlcpy(msg.name, key, sizeof msg.name);
292 strlcpy(msg.value, value, sizeof msg.value);
293
294 return send_prop_msg(&msg);
295 } else {
296 // New protocol only allows long values for ro. properties only.
297 if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
298 // Use proper protocol
Paul Lawrence71ade012023-07-18 08:49:44 -0700299 PropertyServiceConnection connection(key);
Tom Cherrye275d6d2017-12-11 23:31:33 -0800300 if (!connection.IsValid()) {
301 errno = connection.GetLastError();
Elliott Hughes2557f732023-07-12 21:15:23 +0000302 async_safe_format_log(ANDROID_LOG_WARN, "libc",
303 "Unable to set property \"%s\" to \"%s\": connection failed: %m", key,
304 value);
Tom Cherrye275d6d2017-12-11 23:31:33 -0800305 return -1;
306 }
307
308 SocketWriter writer(&connection);
309 if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {
310 errno = connection.GetLastError();
311 async_safe_format_log(ANDROID_LOG_WARN, "libc",
Elliott Hughes2557f732023-07-12 21:15:23 +0000312 "Unable to set property \"%s\" to \"%s\": write failed: %m", key,
313 value);
Tom Cherrye275d6d2017-12-11 23:31:33 -0800314 return -1;
315 }
316
317 int result = -1;
318 if (!connection.RecvInt32(&result)) {
319 errno = connection.GetLastError();
320 async_safe_format_log(ANDROID_LOG_WARN, "libc",
Elliott Hughes2557f732023-07-12 21:15:23 +0000321 "Unable to set property \"%s\" to \"%s\": recv failed: %m", key, value);
Tom Cherrye275d6d2017-12-11 23:31:33 -0800322 return -1;
323 }
324
325 if (result != PROP_SUCCESS) {
326 async_safe_format_log(ANDROID_LOG_WARN, "libc",
Elliott Hughesa9335822024-04-22 21:01:38 +0000327 "Unable to set property \"%s\" to \"%s\": %s (0x%x)", key, value,
328 __prop_error_to_string(result), result);
Tom Cherrye275d6d2017-12-11 23:31:33 -0800329 return -1;
330 }
331
332 return 0;
333 }
334}