xref: /aosp_15_r20/bionic/libc/bionic/system_property_set.cpp (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1 /*
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>
37 #include <sys/system_properties.h>
38 #include <sys/types.h>
39 #include <sys/uio.h>
40 #include <sys/un.h>
41 #include <unistd.h>
42 
43 #include <async_safe/log.h>
44 #include <async_safe/CHECK.h>
45 
46 #include "private/bionic_defs.h"
47 #include "platform/bionic/macros.h"
48 #include "private/ScopedFd.h"
49 
50 static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
51 static const char property_service_for_system_socket[] =
52     "/dev/socket/" PROP_SERVICE_FOR_SYSTEM_NAME;
53 static const char* kServiceVersionPropertyName = "ro.property_service.version";
54 
55 class PropertyServiceConnection {
56  public:
PropertyServiceConnection(const char * name)57   PropertyServiceConnection(const char* name) : last_error_(0) {
58     socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));
59     if (socket_.get() == -1) {
60       last_error_ = errno;
61       return;
62     }
63 
64     // 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);
76     sockaddr_un addr;
77     memset(&addr, 0, sizeof(addr));
78     strlcpy(addr.sun_path, socket, sizeof(addr.sun_path));
79     addr.sun_family = AF_LOCAL;
80     socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
81 
82     if (TEMP_FAILURE_RETRY(connect(socket_.get(),
83                                    reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
84       last_error_ = errno;
85       socket_.reset();
86     }
87   }
88 
IsValid()89   bool IsValid() {
90     return socket_.get() != -1;
91   }
92 
GetLastError()93   int GetLastError() {
94     return last_error_;
95   }
96 
RecvInt32(int32_t * value)97   bool RecvInt32(int32_t* value) {
98     int result = TEMP_FAILURE_RETRY(recv(socket_.get(), value, sizeof(*value), MSG_WAITALL));
99     return CheckSendRecvResult(result, sizeof(*value));
100   }
101 
socket()102   int socket() {
103     return socket_.get();
104   }
105 
106  private:
CheckSendRecvResult(int result,int expected_len)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 
119   ScopedFd socket_;
120   int last_error_;
121 
122   friend class SocketWriter;
123 };
124 
125 class SocketWriter {
126  public:
SocketWriter(PropertyServiceConnection * connection)127   explicit SocketWriter(PropertyServiceConnection* connection)
128       : connection_(connection), iov_index_(0), uint_buf_index_(0) {
129   }
130 
WriteUint32(uint32_t value)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 
WriteString(const char * value)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 
Send()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 
181   BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(SocketWriter);
182 };
183 
184 struct prop_msg {
185   unsigned cmd;
186   char name[PROP_NAME_MAX];
187   char value[PROP_VALUE_MAX];
188 };
189 
send_prop_msg(const prop_msg * msg)190 static int send_prop_msg(const prop_msg* msg) {
191   PropertyServiceConnection connection(msg->name);
192   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.
224       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 
234 static constexpr uint32_t kProtocolVersion1 = 1;
235 static constexpr uint32_t kProtocolVersion2 = 2;  // current
236 
237 static atomic_uint_least32_t g_propservice_protocol_version = 0;
238 
detect_protocol_version()239 static 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 
__prop_error_to_string(int error)259 static 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 
274 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
__system_property_set(const char * key,const char * value)275 int __system_property_set(const char* key, const char* value) {
276   if (key == nullptr) return -1;
277   if (value == nullptr) value = "";
278 
279   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
299     PropertyServiceConnection connection(key);
300     if (!connection.IsValid()) {
301       errno = connection.GetLastError();
302       async_safe_format_log(ANDROID_LOG_WARN, "libc",
303                             "Unable to set property \"%s\" to \"%s\": connection failed: %m", key,
304                             value);
305       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",
312                             "Unable to set property \"%s\" to \"%s\": write failed: %m", key,
313                             value);
314       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",
321                             "Unable to set property \"%s\" to \"%s\": recv failed: %m", key, value);
322       return -1;
323     }
324 
325     if (result != PROP_SUCCESS) {
326       async_safe_format_log(ANDROID_LOG_WARN, "libc",
327                             "Unable to set property \"%s\" to \"%s\": %s (0x%x)", key, value,
328                             __prop_error_to_string(result), result);
329       return -1;
330     }
331 
332     return 0;
333   }
334 }
335