system/core
Révision | ffc2ed986654651d3e72502fb33fd2b2a64ef0ca (tree) |
---|---|
l'heure | 2016-09-15 04:23:21 |
Auteur | Jaap Jan Meijer <jjmeijer88@gmai...> |
Commiter | Jaap Jan Meijer |
Merge remote-tracking branch 'cm/cm-13.0' into cm-13.0-x86
Conflicts:
adb/sockets.cpp
@@ -200,7 +200,10 @@ endif | ||
200 | 200 | # will violate ODR |
201 | 201 | LOCAL_SHARED_LIBRARIES := |
202 | 202 | |
203 | +# Don't build the host adb on Windows (this branch should only be used for security updates.) | |
204 | +ifneq ($(HOST_OS),windows) | |
203 | 205 | include $(BUILD_HOST_EXECUTABLE) |
206 | +endif | |
204 | 207 | |
205 | 208 | $(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) |
206 | 209 |
@@ -6,7 +6,6 @@ | ||
6 | 6 | #ifndef ADB_MUTEX |
7 | 7 | #error ADB_MUTEX not defined when including this file |
8 | 8 | #endif |
9 | -ADB_MUTEX(socket_list_lock) | |
10 | 9 | ADB_MUTEX(transport_lock) |
11 | 10 | ADB_MUTEX(fdevent_lock) |
12 | 11 | #if ADB_HOST |
@@ -25,18 +25,25 @@ | ||
25 | 25 | #include <string.h> |
26 | 26 | #include <unistd.h> |
27 | 27 | |
28 | +#include <algorithm> | |
29 | +#include <mutex> | |
30 | +#include <string> | |
31 | +#include <vector> | |
32 | + | |
28 | 33 | #if !ADB_HOST |
29 | 34 | #include "cutils/properties.h" |
30 | 35 | #endif |
31 | 36 | |
32 | 37 | #include "adb.h" |
33 | 38 | #include "adb_io.h" |
39 | +#include "sysdeps/mutex.h" | |
34 | 40 | #include "transport.h" |
35 | 41 | |
36 | -ADB_MUTEX_DEFINE( socket_list_lock ); | |
37 | - | |
38 | -static void local_socket_close_locked(asocket *s); | |
42 | +#if !defined(__BIONIC__) | |
43 | +using std::recursive_mutex; | |
44 | +#endif | |
39 | 45 | |
46 | +static recursive_mutex& local_socket_list_lock = *new recursive_mutex(); | |
40 | 47 | static unsigned local_socket_next_id = 1; |
41 | 48 | |
42 | 49 | static asocket local_socket_list = { |
@@ -53,13 +60,16 @@ static asocket local_socket_closing_list = { | ||
53 | 60 | .prev = &local_socket_closing_list, |
54 | 61 | }; |
55 | 62 | |
56 | -static asocket * | |
57 | -find_socket_in_list(asocket *list, unsigned local_id, unsigned peer_id) | |
63 | +// Parse the global list of sockets to find one with id |local_id|. | |
64 | +// If |peer_id| is not 0, also check that it is connected to a peer | |
65 | +// with id |peer_id|. Returns an asocket handle on success, NULL on failure. | |
66 | +asocket *find_local_socket(unsigned local_id, unsigned peer_id) | |
58 | 67 | { |
59 | 68 | asocket *s; |
60 | 69 | asocket *result = NULL; |
61 | 70 | |
62 | - for (s = list->next; s != list; s = s->next) { | |
71 | + std::lock_guard<recursive_mutex> lock(local_socket_list_lock); | |
72 | + for (s = local_socket_list.next; s != &local_socket_list; s = s->next) { | |
63 | 73 | if (s->id != local_id) |
64 | 74 | continue; |
65 | 75 | if (peer_id == 0 || (s->peer && s->peer->id == peer_id)) { |
@@ -71,20 +81,6 @@ find_socket_in_list(asocket *list, unsigned local_id, unsigned peer_id) | ||
71 | 81 | return result; |
72 | 82 | } |
73 | 83 | |
74 | - | |
75 | -// Parse the global list of sockets to find one with id |local_id|. | |
76 | -// If |peer_id| is not 0, also check that it is connected to a peer | |
77 | -// with id |peer_id|. Returns an asocket handle on success, NULL on failure. | |
78 | -asocket *find_local_socket(unsigned local_id, unsigned peer_id) | |
79 | -{ | |
80 | - asocket *result = NULL; | |
81 | - | |
82 | - adb_mutex_lock(&socket_list_lock); | |
83 | - result = find_socket_in_list(&local_socket_list, local_id, peer_id); | |
84 | - adb_mutex_unlock(&socket_list_lock); | |
85 | - return result; | |
86 | -} | |
87 | - | |
88 | 84 | static void |
89 | 85 | insert_local_socket(asocket* s, asocket* list) |
90 | 86 | { |
@@ -94,20 +90,17 @@ insert_local_socket(asocket* s, asocket* list) | ||
94 | 90 | s->next->prev = s; |
95 | 91 | } |
96 | 92 | |
97 | - | |
98 | -void install_local_socket(asocket *s) | |
99 | -{ | |
100 | - adb_mutex_lock(&socket_list_lock); | |
93 | +void install_local_socket(asocket* s) { | |
94 | + std::lock_guard<recursive_mutex> lock(local_socket_list_lock); | |
101 | 95 | |
102 | 96 | s->id = local_socket_next_id++; |
103 | 97 | |
104 | 98 | // Socket ids should never be 0. |
105 | - if (local_socket_next_id == 0) | |
106 | - local_socket_next_id = 1; | |
99 | + if (local_socket_next_id == 0) { | |
100 | + fatal("local socket id overflow"); | |
101 | + } | |
107 | 102 | |
108 | 103 | insert_local_socket(s, &local_socket_list); |
109 | - | |
110 | - adb_mutex_unlock(&socket_list_lock); | |
111 | 104 | } |
112 | 105 | |
113 | 106 | void remove_socket(asocket *s) |
@@ -129,30 +122,14 @@ void remove_socket(asocket *s) | ||
129 | 122 | void close_all_sockets(atransport *t) |
130 | 123 | { |
131 | 124 | asocket *s; |
132 | - | |
133 | - D("close all sockets of transport %p\n", t); | |
134 | - /* this is a little gross, but since s->close() *will* modify | |
135 | - ** the list out from under you, your options are limited. | |
136 | - */ | |
137 | - adb_mutex_lock(&socket_list_lock); | |
125 | + std::lock_guard<recursive_mutex> lock(local_socket_list_lock); | |
138 | 126 | restart: |
139 | - for(s = local_socket_list.next; s != &local_socket_list; s = s->next){ | |
140 | - if(s->transport == t || (s->peer && s->peer->transport == t)) { | |
141 | - // set force_close flag since transport is going to be removed. | |
142 | - // we need ensure the socket is closed after we return. | |
143 | - s->force_close = 1; | |
144 | - // avoid race condition with pending fdevent | |
145 | - if (s->running) { | |
146 | - // unlock to give a chance to close socket after running | |
147 | - adb_mutex_unlock(&socket_list_lock); | |
148 | - adb_sleep_ms(10); // sleep to relax cpu | |
149 | - adb_mutex_lock(&socket_list_lock); | |
150 | - } else | |
151 | - local_socket_close_locked(s); | |
127 | + for (s = local_socket_list.next; s != &local_socket_list; s = s->next) { | |
128 | + if (s->transport == t || (s->peer && s->peer->transport == t)) { | |
129 | + s->close(s); | |
152 | 130 | goto restart; |
153 | 131 | } |
154 | 132 | } |
155 | - adb_mutex_unlock(&socket_list_lock); | |
156 | 133 | } |
157 | 134 | |
158 | 135 | static int local_socket_enqueue(asocket *s, apacket *p) |
@@ -215,23 +192,6 @@ static void local_socket_ready(asocket *s) | ||
215 | 192 | fdevent_add(&s->fde, FDE_READ); |
216 | 193 | } |
217 | 194 | |
218 | -static void local_socket_close(asocket *s) | |
219 | -{ | |
220 | - unsigned local_id = s->id; | |
221 | - unsigned peer_id = s->peer ? s->peer->id : 0; | |
222 | - asocket *sk; | |
223 | - | |
224 | - adb_mutex_lock(&socket_list_lock); | |
225 | - // we may race with close_all_sockets (called by input-thread), | |
226 | - // so need to check if socket already destoried. | |
227 | - sk = find_socket_in_list(&local_socket_list, local_id, peer_id); | |
228 | - if (!sk) | |
229 | - sk = find_socket_in_list(&local_socket_closing_list, local_id, peer_id); | |
230 | - if (sk) | |
231 | - local_socket_close_locked(s); | |
232 | - adb_mutex_unlock(&socket_list_lock); | |
233 | -} | |
234 | - | |
235 | 195 | // be sure to hold the socket list lock when calling this |
236 | 196 | static void local_socket_destroy(asocket *s) |
237 | 197 | { |
@@ -260,27 +220,21 @@ static void local_socket_destroy(asocket *s) | ||
260 | 220 | } |
261 | 221 | } |
262 | 222 | |
263 | - | |
264 | -static void local_socket_close_locked(asocket *s) | |
265 | -{ | |
266 | - D("entered local_socket_close_locked. LS(%d) fd=%d\n", s->id, s->fd); | |
267 | - if(s->peer) { | |
268 | - D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n", | |
269 | - s->id, s->peer->id, s->peer->fd); | |
223 | +static void local_socket_close(asocket* s) { | |
224 | + D("entered local_socket_close. LS(%d) fd=%d", s->id, s->fd); | |
225 | + std::lock_guard<recursive_mutex> lock(local_socket_list_lock); | |
226 | + if (s->peer) { | |
227 | + D("LS(%d): closing peer. peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd); | |
270 | 228 | /* Note: it's important to call shutdown before disconnecting from |
271 | 229 | * the peer, this ensures that remote sockets can still get the id |
272 | 230 | * of the local socket they're connected to, to send a CLOSE() |
273 | 231 | * protocol event. */ |
274 | - if (s->peer->shutdown) | |
275 | - s->peer->shutdown(s->peer); | |
276 | - s->peer->peer = 0; | |
277 | - // tweak to avoid deadlock | |
278 | - if (s->peer->close == local_socket_close) { | |
279 | - local_socket_close_locked(s->peer); | |
280 | - } else { | |
281 | - s->peer->close(s->peer); | |
232 | + if (s->peer->shutdown) { | |
233 | + s->peer->shutdown(s->peer); | |
282 | 234 | } |
283 | - s->peer = 0; | |
235 | + s->peer->peer = nullptr; | |
236 | + s->peer->close(s->peer); | |
237 | + s->peer = nullptr; | |
284 | 238 | } |
285 | 239 | |
286 | 240 | /* If we are already closing, or if there are no |
@@ -0,0 +1,143 @@ | ||
1 | +#pragma once | |
2 | + | |
3 | +/* | |
4 | + * Copyright (C) 2016 The Android Open Source Project | |
5 | + * | |
6 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
7 | + * you may not use this file except in compliance with the License. | |
8 | + * You may obtain a copy of the License at | |
9 | + * | |
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | + * | |
12 | + * Unless required by applicable law or agreed to in writing, software | |
13 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
14 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 | + * See the License for the specific language governing permissions and | |
16 | + * limitations under the License. | |
17 | + */ | |
18 | + | |
19 | +#if defined(_WIN32) | |
20 | + | |
21 | +#include <windows.h> | |
22 | + | |
23 | +#include <android-base/macros.h> | |
24 | + | |
25 | +#include "adb.h" | |
26 | + | |
27 | +// The prebuilt version of mingw we use doesn't support mutex or recursive_mutex. | |
28 | +// Therefore, implement our own using the Windows primitives. | |
29 | +// Put them directly into the std namespace, so that when they're actually available, the build | |
30 | +// breaks until they're removed. | |
31 | + | |
32 | +#include <mutex> | |
33 | +namespace std { | |
34 | + | |
35 | +// CRITICAL_SECTION is recursive, so just wrap it in a Mutex-compatible class. | |
36 | +class recursive_mutex { | |
37 | + public: | |
38 | + recursive_mutex() { | |
39 | + InitializeCriticalSection(&mutex_); | |
40 | + } | |
41 | + | |
42 | + ~recursive_mutex() { | |
43 | + DeleteCriticalSection(&mutex_); | |
44 | + } | |
45 | + | |
46 | + void lock() { | |
47 | + EnterCriticalSection(&mutex_); | |
48 | + } | |
49 | + | |
50 | + bool try_lock() { | |
51 | + return TryEnterCriticalSection(&mutex_); | |
52 | + } | |
53 | + | |
54 | + void unlock() { | |
55 | + LeaveCriticalSection(&mutex_); | |
56 | + } | |
57 | + | |
58 | + private: | |
59 | + CRITICAL_SECTION mutex_; | |
60 | + | |
61 | + DISALLOW_COPY_AND_ASSIGN(recursive_mutex); | |
62 | +}; | |
63 | + | |
64 | +class mutex { | |
65 | + public: | |
66 | + mutex() { | |
67 | + } | |
68 | + | |
69 | + ~mutex() { | |
70 | + } | |
71 | + | |
72 | + void lock() { | |
73 | + mutex_.lock(); | |
74 | + if (++lock_count_ != 1) { | |
75 | + fatal("non-recursive mutex locked reentrantly"); | |
76 | + } | |
77 | + } | |
78 | + | |
79 | + void unlock() { | |
80 | + if (--lock_count_ != 0) { | |
81 | + fatal("non-recursive mutex unlock resulted in unexpected lock count: %d", lock_count_); | |
82 | + } | |
83 | + mutex_.unlock(); | |
84 | + } | |
85 | + | |
86 | + bool try_lock() { | |
87 | + if (!mutex_.try_lock()) { | |
88 | + return false; | |
89 | + } | |
90 | + | |
91 | + if (lock_count_ != 0) { | |
92 | + mutex_.unlock(); | |
93 | + return false; | |
94 | + } | |
95 | + | |
96 | + ++lock_count_; | |
97 | + return true; | |
98 | + } | |
99 | + | |
100 | + private: | |
101 | + recursive_mutex mutex_; | |
102 | + size_t lock_count_ = 0; | |
103 | +}; | |
104 | + | |
105 | +} | |
106 | + | |
107 | +#elif defined(__BIONIC__) | |
108 | + | |
109 | +// On M, the recovery image uses parts of adb that depends on recursive_mutex, and uses libstdc++, | |
110 | +// which lacks it. | |
111 | + | |
112 | +#include <pthread.h> | |
113 | +#include <mutex> | |
114 | + | |
115 | +#include <base/macros.h> | |
116 | + | |
117 | +class recursive_mutex { | |
118 | + public: | |
119 | + recursive_mutex() { | |
120 | + } | |
121 | + | |
122 | + ~recursive_mutex() { | |
123 | + } | |
124 | + | |
125 | + void lock() { | |
126 | + pthread_mutex_lock(&mutex_); | |
127 | + } | |
128 | + | |
129 | + bool try_lock() { | |
130 | + return pthread_mutex_trylock(&mutex_); | |
131 | + } | |
132 | + | |
133 | + void unlock() { | |
134 | + pthread_mutex_unlock(&mutex_); | |
135 | + } | |
136 | + | |
137 | + private: | |
138 | + pthread_mutex_t mutex_ = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; | |
139 | + | |
140 | + DISALLOW_COPY_AND_ASSIGN(recursive_mutex); | |
141 | +}; | |
142 | + | |
143 | +#endif |
@@ -67,8 +67,8 @@ static void dump_process_footer(log_t* log, pid_t pid) { | ||
67 | 67 | _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid); |
68 | 68 | } |
69 | 69 | |
70 | -static void dump_thread( | |
71 | - log_t* log, pid_t tid, bool attached, bool* detach_failed, int* total_sleep_time_usec) { | |
70 | +static void dump_thread(log_t* log, pid_t pid, pid_t tid, bool attached, | |
71 | + bool* detach_failed, int* total_sleep_time_usec) { | |
72 | 72 | char path[PATH_MAX]; |
73 | 73 | char threadnamebuf[1024]; |
74 | 74 | char* threadname = NULL; |
@@ -88,7 +88,7 @@ static void dump_thread( | ||
88 | 88 | |
89 | 89 | _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid); |
90 | 90 | |
91 | - if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { | |
91 | + if (!attached && !ptrace_attach_thread(pid, tid)) { | |
92 | 92 | _LOG(log, logtype::BACKTRACE, "Could not attach to thread: %s\n", strerror(errno)); |
93 | 93 | return; |
94 | 94 | } |
@@ -117,7 +117,7 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, | ||
117 | 117 | log.amfd = amfd; |
118 | 118 | |
119 | 119 | dump_process_header(&log, pid); |
120 | - dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec); | |
120 | + dump_thread(&log, pid, tid, true, detach_failed, total_sleep_time_usec); | |
121 | 121 | |
122 | 122 | char task_path[64]; |
123 | 123 | snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); |
@@ -135,7 +135,7 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, | ||
135 | 135 | continue; |
136 | 136 | } |
137 | 137 | |
138 | - dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec); | |
138 | + dump_thread(&log, pid, new_tid, false, detach_failed, total_sleep_time_usec); | |
139 | 139 | } |
140 | 140 | closedir(d); |
141 | 141 | } |
@@ -308,15 +308,13 @@ static int read_request(int fd, debugger_request_t* out_request) { | ||
308 | 308 | |
309 | 309 | if (msg.action == DEBUGGER_ACTION_CRASH) { |
310 | 310 | // Ensure that the tid reported by the crashing process is valid. |
311 | - char buf[64]; | |
312 | - struct stat s; | |
313 | - enable_etb_trace(cr); | |
314 | - snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); | |
315 | - if (stat(buf, &s)) { | |
311 | + // This check needs to happen again after ptracing the requested thread to prevent a race. | |
312 | + if (!pid_contains_tid(out_request->pid, out_request->tid)) { | |
316 | 313 | ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", |
317 | - out_request->tid, out_request->pid); | |
314 | + out_request->tid, out_request->pid); | |
318 | 315 | return -1; |
319 | 316 | } |
317 | + enable_etb_trace(cr); | |
320 | 318 | } else if (cr.uid == 0 |
321 | 319 | || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) { |
322 | 320 | // Only root or system can ask us to attach to any process and dump it explicitly. |
@@ -476,9 +474,32 @@ static void handle_request(int fd) { | ||
476 | 474 | // ensure that it can run as soon as we call PTRACE_CONT below. |
477 | 475 | // See details in bionic/libc/linker/debugger.c, in function |
478 | 476 | // debugger_signal_handler(). |
479 | - if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { | |
477 | + if (!ptrace_attach_thread(request.pid, request.tid)) { | |
480 | 478 | ALOGE("ptrace attach failed: %s\n", strerror(errno)); |
481 | 479 | } else { |
480 | + // DEBUGGER_ACTION_CRASH requests can come from arbitrary processes and the tid field in | |
481 | + // the request is sent from the other side. If an attacker can cause a process to be | |
482 | + // spawned with the pid of their process, they could trick debuggerd into dumping that | |
483 | + // process by exiting after sending the request. Validate the trusted request.uid/gid | |
484 | + // to defend against this. | |
485 | + if (request.action == DEBUGGER_ACTION_CRASH) { | |
486 | + pid_t pid; | |
487 | + uid_t uid; | |
488 | + gid_t gid; | |
489 | + if (get_process_info(request.tid, &pid, &uid, &gid) != 0) { | |
490 | + ALOGE("debuggerd: failed to get process info for tid '%d'", request.tid); | |
491 | + exit(1); | |
492 | + } | |
493 | + | |
494 | + if (pid != request.pid || uid != request.uid || gid != request.gid) { | |
495 | + ALOGE( | |
496 | + "debuggerd: attached task %d does not match request: " | |
497 | + "expected pid=%d,uid=%d,gid=%d, actual pid=%d,uid=%d,gid=%d", | |
498 | + request.tid, request.pid, request.uid, request.gid, pid, uid, gid); | |
499 | + exit(1); | |
500 | + } | |
501 | + } | |
502 | + | |
482 | 503 | bool detach_failed = false; |
483 | 504 | bool tid_unresponsive = false; |
484 | 505 | bool attach_gdb = should_attach_gdb(&request); |
@@ -450,7 +450,7 @@ static bool dump_sibling_thread_report( | ||
450 | 450 | } |
451 | 451 | |
452 | 452 | // Skip this thread if cannot ptrace it |
453 | - if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) { | |
453 | + if (!ptrace_attach_thread(pid, new_tid)) { | |
454 | 454 | _LOG(log, logtype::ERROR, "ptrace attach to %d failed: %s\n", new_tid, strerror(errno)); |
455 | 455 | continue; |
456 | 456 | } |
@@ -20,6 +20,7 @@ | ||
20 | 20 | |
21 | 21 | #include <errno.h> |
22 | 22 | #include <signal.h> |
23 | +#include <stdlib.h> | |
23 | 24 | #include <string.h> |
24 | 25 | #include <unistd.h> |
25 | 26 | #include <sys/ptrace.h> |
@@ -207,3 +208,31 @@ void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* f | ||
207 | 208 | _LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str()); |
208 | 209 | } |
209 | 210 | } |
211 | + | |
212 | +bool pid_contains_tid(pid_t pid, pid_t tid) { | |
213 | + char task_path[PATH_MAX]; | |
214 | + if (snprintf(task_path, PATH_MAX, "/proc/%d/task/%d", pid, tid) >= PATH_MAX) { | |
215 | + ALOGE("debuggerd: task path overflow (pid = %d, tid = %d)\n", pid, tid); | |
216 | + exit(1); | |
217 | + } | |
218 | + | |
219 | + return access(task_path, F_OK) == 0; | |
220 | +} | |
221 | + | |
222 | +// Attach to a thread, and verify that it's still a member of the given process | |
223 | +bool ptrace_attach_thread(pid_t pid, pid_t tid) { | |
224 | + if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) { | |
225 | + return false; | |
226 | + } | |
227 | + | |
228 | + // Make sure that the task we attached to is actually part of the pid we're dumping. | |
229 | + if (!pid_contains_tid(pid, tid)) { | |
230 | + if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { | |
231 | + ALOGE("debuggerd: failed to detach from thread '%d'", tid); | |
232 | + exit(1); | |
233 | + } | |
234 | + return false; | |
235 | + } | |
236 | + | |
237 | + return true; | |
238 | +} |
@@ -79,4 +79,9 @@ int wait_for_sigstop(pid_t, int*, bool*); | ||
79 | 79 | |
80 | 80 | void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...); |
81 | 81 | |
82 | +bool pid_contains_tid(pid_t pid, pid_t tid); | |
83 | + | |
84 | +// Attach to a thread, and verify that it's still a member of the given process | |
85 | +bool ptrace_attach_thread(pid_t pid, pid_t tid); | |
86 | + | |
82 | 87 | #endif // _DEBUGGERD_UTILITY_H |
@@ -87,7 +87,7 @@ ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len); | ||
87 | 87 | * "dst" becomes \xE3\x81\x82\xE3\x81\x84 |
88 | 88 | * (note that "dst" is NOT null-terminated, like strncpy) |
89 | 89 | */ |
90 | -void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst); | |
90 | +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len); | |
91 | 91 | |
92 | 92 | /** |
93 | 93 | * Returns the unicode value at "index". |
@@ -109,7 +109,7 @@ ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len); | ||
109 | 109 | * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added |
110 | 110 | * NULL terminator. |
111 | 111 | */ |
112 | -void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst); | |
112 | +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len); | |
113 | 113 | |
114 | 114 | /** |
115 | 115 | * Returns the length of "src" when "src" is valid UTF-8 string. |
@@ -102,20 +102,21 @@ static char* allocFromUTF16(const char16_t* in, size_t len) | ||
102 | 102 | { |
103 | 103 | if (len == 0) return getEmptyString(); |
104 | 104 | |
105 | - const ssize_t bytes = utf16_to_utf8_length(in, len); | |
106 | - if (bytes < 0) { | |
105 | + // Allow for closing '\0' | |
106 | + const ssize_t resultStrLen = utf16_to_utf8_length(in, len) + 1; | |
107 | + if (resultStrLen < 1) { | |
107 | 108 | return getEmptyString(); |
108 | 109 | } |
109 | 110 | |
110 | - SharedBuffer* buf = SharedBuffer::alloc(bytes+1); | |
111 | + SharedBuffer* buf = SharedBuffer::alloc(resultStrLen); | |
111 | 112 | ALOG_ASSERT(buf, "Unable to allocate shared buffer"); |
112 | 113 | if (!buf) { |
113 | 114 | return getEmptyString(); |
114 | 115 | } |
115 | 116 | |
116 | - char* str = (char*)buf->data(); | |
117 | - utf16_to_utf8(in, len, str); | |
118 | - return str; | |
117 | + char* resultStr = (char*)buf->data(); | |
118 | + utf16_to_utf8(in, len, resultStr, resultStrLen); | |
119 | + return resultStr; | |
119 | 120 | } |
120 | 121 | |
121 | 122 | static char* allocFromUTF32(const char32_t* in, size_t len) |
@@ -124,21 +125,21 @@ static char* allocFromUTF32(const char32_t* in, size_t len) | ||
124 | 125 | return getEmptyString(); |
125 | 126 | } |
126 | 127 | |
127 | - const ssize_t bytes = utf32_to_utf8_length(in, len); | |
128 | - if (bytes < 0) { | |
128 | + const ssize_t resultStrLen = utf32_to_utf8_length(in, len) + 1; | |
129 | + if (resultStrLen < 1) { | |
129 | 130 | return getEmptyString(); |
130 | 131 | } |
131 | 132 | |
132 | - SharedBuffer* buf = SharedBuffer::alloc(bytes+1); | |
133 | + SharedBuffer* buf = SharedBuffer::alloc(resultStrLen); | |
133 | 134 | ALOG_ASSERT(buf, "Unable to allocate shared buffer"); |
134 | 135 | if (!buf) { |
135 | 136 | return getEmptyString(); |
136 | 137 | } |
137 | 138 | |
138 | - char* str = (char*) buf->data(); | |
139 | - utf32_to_utf8(in, len, str); | |
139 | + char* resultStr = (char*) buf->data(); | |
140 | + utf32_to_utf8(in, len, resultStr, resultStrLen); | |
140 | 141 | |
141 | - return str; | |
142 | + return resultStr; | |
142 | 143 | } |
143 | 144 | |
144 | 145 | // --------------------------------------------------------------------------- |
@@ -14,6 +14,7 @@ | ||
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 16 | |
17 | +#include <log/log.h> | |
17 | 18 | #include <utils/Unicode.h> |
18 | 19 | |
19 | 20 | #include <stddef.h> |
@@ -182,7 +183,7 @@ ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) | ||
182 | 183 | return ret; |
183 | 184 | } |
184 | 185 | |
185 | -void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) | |
186 | +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len) | |
186 | 187 | { |
187 | 188 | if (src == NULL || src_len == 0 || dst == NULL) { |
188 | 189 | return; |
@@ -193,9 +194,12 @@ void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) | ||
193 | 194 | char *cur = dst; |
194 | 195 | while (cur_utf32 < end_utf32) { |
195 | 196 | size_t len = utf32_codepoint_utf8_length(*cur_utf32); |
197 | + LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len); | |
196 | 198 | utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); |
197 | 199 | cur += len; |
200 | + dst_len -= len; | |
198 | 201 | } |
202 | + LOG_ALWAYS_FATAL_IF(dst_len < 1, "dst_len < 1: %zu < 1", dst_len); | |
199 | 203 | *cur = '\0'; |
200 | 204 | } |
201 | 205 |
@@ -324,7 +328,7 @@ int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2 | ||
324 | 328 | : 0); |
325 | 329 | } |
326 | 330 | |
327 | -void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) | |
331 | +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len) | |
328 | 332 | { |
329 | 333 | if (src == NULL || src_len == 0 || dst == NULL) { |
330 | 334 | return; |
@@ -345,9 +349,12 @@ void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) | ||
345 | 349 | utf32 = (char32_t) *cur_utf16++; |
346 | 350 | } |
347 | 351 | const size_t len = utf32_codepoint_utf8_length(utf32); |
352 | + LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len); | |
348 | 353 | utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); |
349 | 354 | cur += len; |
355 | + dst_len -= len; | |
350 | 356 | } |
357 | + LOG_ALWAYS_FATAL_IF(dst_len < 1, "%zu < 1", dst_len); | |
351 | 358 | *cur = '\0'; |
352 | 359 | } |
353 | 360 |
@@ -408,10 +415,10 @@ ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) | ||
408 | 415 | const char16_t* const end = src + src_len; |
409 | 416 | while (src < end) { |
410 | 417 | if ((*src & 0xFC00) == 0xD800 && (src + 1) < end |
411 | - && (*++src & 0xFC00) == 0xDC00) { | |
418 | + && (*(src + 1) & 0xFC00) == 0xDC00) { | |
412 | 419 | // surrogate pairs are always 4 bytes. |
413 | 420 | ret += 4; |
414 | - src++; | |
421 | + src += 2; | |
415 | 422 | } else { |
416 | 423 | ret += utf32_codepoint_utf8_length((char32_t) *src++); |
417 | 424 | } |
@@ -17,6 +17,7 @@ | ||
17 | 17 | #define LOG_TAG "String8_test" |
18 | 18 | #include <utils/Log.h> |
19 | 19 | #include <utils/String8.h> |
20 | +#include <utils/String16.h> | |
20 | 21 | |
21 | 22 | #include <gtest/gtest.h> |
22 | 23 |
@@ -72,4 +73,22 @@ TEST_F(String8Test, OperatorPlusEquals) { | ||
72 | 73 | EXPECT_STREQ(src3, " Verify me."); |
73 | 74 | } |
74 | 75 | |
76 | +// http://b/29250543 | |
77 | +TEST_F(String8Test, CorrectInvalidSurrogate) { | |
78 | + // d841d8 is an invalid start for a surrogate pair. Make sure this is handled by ignoring the | |
79 | + // first character in the pair and handling the rest correctly. | |
80 | + String16 string16(u"\xd841\xd841\xdc41\x0000"); | |
81 | + String8 string8(string16); | |
82 | + | |
83 | + EXPECT_EQ(4U, string8.length()); | |
84 | +} | |
85 | + | |
86 | +TEST_F(String8Test, CheckUtf32Conversion) { | |
87 | + // Since bound checks were added, check the conversion can be done without fatal errors. | |
88 | + // The utf8 lengths of these are chars are 1 + 2 + 3 + 4 = 10. | |
89 | + const char32_t string32[] = U"\x0000007f\x000007ff\x0000911\x0010fffe"; | |
90 | + String8 string8(string32); | |
91 | + EXPECT_EQ(10U, string8.length()); | |
92 | +} | |
93 | + | |
75 | 94 | } |