ptracer.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. // Copyright 2017 The Crashpad Authors. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "util/linux/ptracer.h"
  15. #include <errno.h>
  16. #include <linux/elf.h>
  17. #include <string.h>
  18. #include <sys/ptrace.h>
  19. #include <sys/uio.h>
  20. #include "base/logging.h"
  21. #include "build/build_config.h"
  22. #include "util/misc/from_pointer_cast.h"
  23. #if defined(ARCH_CPU_X86_FAMILY)
  24. #include <asm/ldt.h>
  25. #endif
  26. namespace crashpad {
  27. namespace {
  28. #if defined(ARCH_CPU_X86_FAMILY)
  29. template <typename Destination>
  30. bool GetRegisterSet(pid_t tid, int set, Destination* dest, bool can_log) {
  31. iovec iov;
  32. iov.iov_base = dest;
  33. iov.iov_len = sizeof(*dest);
  34. if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(set), &iov) != 0) {
  35. PLOG_IF(ERROR, can_log) << "ptrace";
  36. return false;
  37. }
  38. if (iov.iov_len != sizeof(*dest)) {
  39. LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
  40. << " != " << sizeof(*dest);
  41. return false;
  42. }
  43. return true;
  44. }
  45. bool GetFloatingPointRegisters32(pid_t tid,
  46. FloatContext* context,
  47. bool can_log) {
  48. return GetRegisterSet(tid, NT_PRXFPREG, &context->f32.fxsave, can_log);
  49. }
  50. bool GetFloatingPointRegisters64(pid_t tid,
  51. FloatContext* context,
  52. bool can_log) {
  53. return GetRegisterSet(tid, NT_PRFPREG, &context->f64.fxsave, can_log);
  54. }
  55. bool GetThreadArea32(pid_t tid,
  56. const ThreadContext& context,
  57. LinuxVMAddress* address,
  58. bool can_log) {
  59. size_t index = (context.t32.xgs & 0xffff) >> 3;
  60. user_desc desc;
  61. if (ptrace(
  62. PTRACE_GET_THREAD_AREA, tid, reinterpret_cast<void*>(index), &desc) !=
  63. 0) {
  64. PLOG_IF(ERROR, can_log) << "ptrace";
  65. return false;
  66. }
  67. *address = desc.base_addr;
  68. return true;
  69. }
  70. bool GetThreadArea64(pid_t tid,
  71. const ThreadContext& context,
  72. LinuxVMAddress* address,
  73. bool can_log) {
  74. *address = context.t64.fs_base;
  75. return true;
  76. }
  77. #elif defined(ARCH_CPU_ARM_FAMILY)
  78. #if defined(ARCH_CPU_ARMEL)
  79. // PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel
  80. // support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including
  81. // x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for ARM only since
  82. // Linux 3.5.0 (0693bf68148c4). Older Linux kernels support PTRACE_GETREGS,
  83. // PTRACE_GETFPREGS, and PTRACE_GETVFPREGS instead, which don't allow checking
  84. // the size of data copied.
  85. //
  86. // Fortunately, 64-bit ARM support only appeared in Linux 3.7.0, so if
  87. // PTRACE_GETREGSET fails on ARM with EIO, indicating that the request is not
  88. // supported, the kernel must be old enough that 64-bit ARM isn’t supported
  89. // either.
  90. //
  91. // TODO(mark): Once helpers to interpret the kernel version are available, add
  92. // a DCHECK to ensure that the kernel is older than 3.5.
  93. bool GetGeneralPurposeRegistersLegacy(pid_t tid,
  94. ThreadContext* context,
  95. bool can_log) {
  96. if (ptrace(PTRACE_GETREGS, tid, nullptr, &context->t32) != 0) {
  97. PLOG_IF(ERROR, can_log) << "ptrace";
  98. return false;
  99. }
  100. return true;
  101. }
  102. bool GetFloatingPointRegistersLegacy(pid_t tid,
  103. FloatContext* context,
  104. bool can_log) {
  105. if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
  106. PLOG_IF(ERROR, can_log) << "ptrace";
  107. return false;
  108. }
  109. context->f32.have_fpregs = true;
  110. if (ptrace(PTRACE_GETVFPREGS, tid, nullptr, &context->f32.vfp) != 0) {
  111. switch (errno) {
  112. case EINVAL:
  113. // These registers are optional on 32-bit ARM cpus
  114. break;
  115. default:
  116. PLOG_IF(ERROR, can_log) << "ptrace";
  117. return false;
  118. }
  119. } else {
  120. context->f32.have_vfp = true;
  121. }
  122. return true;
  123. }
  124. #endif // ARCH_CPU_ARMEL
  125. // Normally, the Linux kernel will copy out register sets according to the size
  126. // of the struct that contains them. Tracing a 32-bit ARM process running in
  127. // compatibility mode on a 64-bit ARM cpu will only copy data for the number of
  128. // registers times the size of the register, which won't include any possible
  129. // trailing padding in the struct. These are the sizes of the register data, not
  130. // including any possible padding.
  131. constexpr size_t kArmVfpSize = 32 * 8 + 4;
  132. // Target is 32-bit
  133. bool GetFloatingPointRegisters32(pid_t tid,
  134. FloatContext* context,
  135. bool can_log) {
  136. context->f32.have_fpregs = false;
  137. context->f32.have_vfp = false;
  138. iovec iov;
  139. iov.iov_base = &context->f32.fpregs;
  140. iov.iov_len = sizeof(context->f32.fpregs);
  141. if (ptrace(
  142. PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
  143. 0) {
  144. switch (errno) {
  145. #if defined(ARCH_CPU_ARMEL)
  146. case EIO:
  147. return GetFloatingPointRegistersLegacy(tid, context, can_log);
  148. #endif // ARCH_CPU_ARMEL
  149. case EINVAL:
  150. // A 32-bit process running on a 64-bit CPU doesn't have this register
  151. // set. It should have a VFP register set instead.
  152. break;
  153. default:
  154. PLOG_IF(ERROR, can_log) << "ptrace";
  155. return false;
  156. }
  157. } else {
  158. if (iov.iov_len != sizeof(context->f32.fpregs)) {
  159. LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
  160. << " != " << sizeof(context->f32.fpregs);
  161. return false;
  162. }
  163. context->f32.have_fpregs = true;
  164. }
  165. iov.iov_base = &context->f32.vfp;
  166. iov.iov_len = sizeof(context->f32.vfp);
  167. if (ptrace(
  168. PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_ARM_VFP), &iov) !=
  169. 0) {
  170. switch (errno) {
  171. case EINVAL:
  172. // VFP may not be present on 32-bit ARM cpus.
  173. break;
  174. default:
  175. PLOG_IF(ERROR, can_log) << "ptrace";
  176. return false;
  177. }
  178. } else {
  179. if (iov.iov_len != kArmVfpSize && iov.iov_len != sizeof(context->f32.vfp)) {
  180. LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
  181. << " != " << sizeof(context->f32.vfp);
  182. return false;
  183. }
  184. context->f32.have_vfp = true;
  185. }
  186. if (!(context->f32.have_fpregs || context->f32.have_vfp)) {
  187. LOG_IF(ERROR, can_log) << "Unable to collect registers";
  188. return false;
  189. }
  190. return true;
  191. }
  192. bool GetFloatingPointRegisters64(pid_t tid,
  193. FloatContext* context,
  194. bool can_log) {
  195. iovec iov;
  196. iov.iov_base = context;
  197. iov.iov_len = sizeof(*context);
  198. if (ptrace(
  199. PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
  200. 0) {
  201. PLOG_IF(ERROR, can_log) << "ptrace";
  202. return false;
  203. }
  204. if (iov.iov_len != sizeof(context->f64)) {
  205. LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
  206. << " != " << sizeof(context->f64);
  207. return false;
  208. }
  209. return true;
  210. }
  211. bool GetThreadArea32(pid_t tid,
  212. const ThreadContext& context,
  213. LinuxVMAddress* address,
  214. bool can_log) {
  215. #if defined(ARCH_CPU_ARMEL)
  216. void* result;
  217. if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {
  218. PLOG_IF(ERROR, can_log) << "ptrace";
  219. return false;
  220. }
  221. *address = FromPointerCast<LinuxVMAddress>(result);
  222. return true;
  223. #else
  224. // TODO(jperaza): it doesn't look like there is a way for a 64-bit ARM process
  225. // to get the thread area for a 32-bit ARM process with ptrace.
  226. LOG_IF(WARNING, can_log)
  227. << "64-bit ARM cannot trace TLS area for a 32-bit process";
  228. return false;
  229. #endif // ARCH_CPU_ARMEL
  230. }
  231. bool GetThreadArea64(pid_t tid,
  232. const ThreadContext& context,
  233. LinuxVMAddress* address,
  234. bool can_log) {
  235. iovec iov;
  236. iov.iov_base = address;
  237. iov.iov_len = sizeof(*address);
  238. if (ptrace(
  239. PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_ARM_TLS), &iov) !=
  240. 0) {
  241. PLOG_IF(ERROR, can_log) << "ptrace";
  242. return false;
  243. }
  244. if (iov.iov_len != 8) {
  245. LOG_IF(ERROR, can_log) << "address size mismatch";
  246. return false;
  247. }
  248. return true;
  249. }
  250. #elif defined(ARCH_CPU_MIPS_FAMILY)
  251. // PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel
  252. // support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including
  253. // x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for MIPS only since
  254. // Linux 3.13 (c0ff3c53d4f99). Older Linux kernels support PTRACE_GETREGS,
  255. // and PTRACE_GETFPREGS instead, which don't allow checking the size of data
  256. // copied. Also, PTRACE_GETREGS assumes register size of 64 bits even for 32 bit
  257. // MIPS CPU (contrary to PTRACE_GETREGSET behavior), so we need buffer
  258. // structure here.
  259. bool GetGeneralPurposeRegistersLegacy(pid_t tid,
  260. ThreadContext* context,
  261. bool can_log) {
  262. ThreadContext context_buffer;
  263. if (ptrace(PTRACE_GETREGS, tid, nullptr, &context_buffer.t64) != 0) {
  264. PLOG_IF(ERROR, can_log) << "ptrace";
  265. return false;
  266. }
  267. // Bitness of target process can't be determined through ptrace here, so we
  268. // assume target process has the same as current process, making cross-bit
  269. // ptrace unsupported on MIPS for kernels older than 3.13
  270. #if defined(ARCH_CPU_MIPSEL)
  271. #define THREAD_CONTEXT_FIELD t32
  272. #elif defined(ARCH_CPU_MIPS64EL)
  273. #define THREAD_CONTEXT_FIELD t64
  274. #endif
  275. for (size_t reg = 0; reg < 32; ++reg) {
  276. context->THREAD_CONTEXT_FIELD.regs[reg] = context_buffer.t64.regs[reg];
  277. }
  278. context->THREAD_CONTEXT_FIELD.lo = context_buffer.t64.lo;
  279. context->THREAD_CONTEXT_FIELD.hi = context_buffer.t64.hi;
  280. context->THREAD_CONTEXT_FIELD.cp0_epc = context_buffer.t64.cp0_epc;
  281. context->THREAD_CONTEXT_FIELD.cp0_badvaddr = context_buffer.t64.cp0_badvaddr;
  282. context->THREAD_CONTEXT_FIELD.cp0_status = context_buffer.t64.cp0_status;
  283. context->THREAD_CONTEXT_FIELD.cp0_cause = context_buffer.t64.cp0_cause;
  284. #undef THREAD_CONTEXT_FIELD
  285. return true;
  286. }
  287. bool GetFloatingPointRegistersLegacy(pid_t tid,
  288. FloatContext* context,
  289. bool can_log) {
  290. if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
  291. PLOG_IF(ERROR, can_log) << "ptrace";
  292. return false;
  293. }
  294. return true;
  295. }
  296. bool GetFloatingPointRegisters32(pid_t tid,
  297. FloatContext* context,
  298. bool can_log) {
  299. iovec iov;
  300. iov.iov_base = &context->f32.fpregs;
  301. iov.iov_len = sizeof(context->f32.fpregs);
  302. if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
  303. switch (errno) {
  304. case EINVAL:
  305. // fp may not be present
  306. break;
  307. case EIO:
  308. return GetFloatingPointRegistersLegacy(tid, context, can_log);
  309. default:
  310. PLOG_IF(ERROR, can_log) << "ptrace";
  311. return false;
  312. }
  313. }
  314. return true;
  315. }
  316. bool GetFloatingPointRegisters64(pid_t tid,
  317. FloatContext* context,
  318. bool can_log) {
  319. iovec iov;
  320. iov.iov_base = &context->f64.fpregs;
  321. iov.iov_len = sizeof(context->f64.fpregs);
  322. if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f64.fpregs) != 0) {
  323. switch (errno) {
  324. case EINVAL:
  325. // fp may not be present
  326. break;
  327. case EIO:
  328. return GetFloatingPointRegistersLegacy(tid, context, can_log);
  329. default:
  330. PLOG_IF(ERROR, can_log) << "ptrace";
  331. return false;
  332. }
  333. }
  334. return true;
  335. }
  336. bool GetThreadArea32(pid_t tid,
  337. const ThreadContext& context,
  338. LinuxVMAddress* address,
  339. bool can_log) {
  340. #if defined(ARCH_CPU_MIPSEL)
  341. void* result;
  342. if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {
  343. PLOG_IF(ERROR, can_log) << "ptrace";
  344. return false;
  345. }
  346. *address = FromPointerCast<LinuxVMAddress>(result);
  347. return true;
  348. #else
  349. return false;
  350. #endif
  351. }
  352. bool GetThreadArea64(pid_t tid,
  353. const ThreadContext& context,
  354. LinuxVMAddress* address,
  355. bool can_log) {
  356. void* result;
  357. #if defined(ARCH_CPU_MIPSEL)
  358. if (ptrace(PTRACE_GET_THREAD_AREA_3264, tid, nullptr, &result) != 0) {
  359. #else
  360. if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {
  361. #endif
  362. PLOG_IF(ERROR, can_log) << "ptrace";
  363. return false;
  364. }
  365. *address = FromPointerCast<LinuxVMAddress>(result);
  366. return true;
  367. }
  368. #elif defined(ARCH_CPU_RISCV_FAMILY)
  369. template <typename Destination>
  370. bool GetRegisterSet(pid_t tid, int set, Destination* dest, bool can_log) {
  371. iovec iov;
  372. iov.iov_base = dest;
  373. iov.iov_len = sizeof(*dest);
  374. if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(set), &iov) != 0) {
  375. PLOG_IF(ERROR, can_log) << "ptrace";
  376. return false;
  377. }
  378. if (iov.iov_len != sizeof(*dest)) {
  379. LOG_IF(ERROR, can_log) << "Unexpected registers size";
  380. return false;
  381. }
  382. return true;
  383. }
  384. bool GetFloatingPointRegisters32(pid_t tid,
  385. FloatContext* context,
  386. bool can_log) {
  387. return false;
  388. }
  389. bool GetFloatingPointRegisters64(pid_t tid,
  390. FloatContext* context,
  391. bool can_log) {
  392. return GetRegisterSet(tid, NT_PRFPREG, &context->f64.f, can_log);
  393. }
  394. bool GetThreadArea32(pid_t tid,
  395. const ThreadContext& context,
  396. LinuxVMAddress* address,
  397. bool can_log) {
  398. return false;
  399. }
  400. bool GetThreadArea64(pid_t tid,
  401. const ThreadContext& context,
  402. LinuxVMAddress* address,
  403. bool can_log) {
  404. *address = context.t64.tp;
  405. return true;
  406. }
  407. #else
  408. #error Port.
  409. #endif // ARCH_CPU_X86_FAMILY
  410. size_t GetGeneralPurposeRegistersAndLength(pid_t tid,
  411. ThreadContext* context,
  412. bool can_log) {
  413. iovec iov;
  414. iov.iov_base = context;
  415. iov.iov_len = sizeof(*context);
  416. if (ptrace(
  417. PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iov) !=
  418. 0) {
  419. switch (errno) {
  420. #if defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_MIPS_FAMILY)
  421. case EIO:
  422. return GetGeneralPurposeRegistersLegacy(tid, context, can_log)
  423. ? sizeof(context->t32)
  424. : 0;
  425. #endif // ARCH_CPU_ARMEL
  426. default:
  427. PLOG_IF(ERROR, can_log) << "ptrace";
  428. return 0;
  429. }
  430. }
  431. return iov.iov_len;
  432. }
  433. bool GetGeneralPurposeRegisters32(pid_t tid,
  434. ThreadContext* context,
  435. bool can_log) {
  436. size_t length = GetGeneralPurposeRegistersAndLength(tid, context, can_log);
  437. if (length != sizeof(context->t32)) {
  438. LOG_IF(ERROR, can_log) << "Unexpected registers size " << length
  439. << " != " << sizeof(context->t32);
  440. return false;
  441. }
  442. return true;
  443. }
  444. bool GetGeneralPurposeRegisters64(pid_t tid,
  445. ThreadContext* context,
  446. bool can_log) {
  447. size_t length = GetGeneralPurposeRegistersAndLength(tid, context, can_log);
  448. if (length != sizeof(context->t64)) {
  449. LOG_IF(ERROR, can_log) << "Unexpected registers size " << length
  450. << " != " << sizeof(context->t64);
  451. return false;
  452. }
  453. return true;
  454. }
  455. } // namespace
  456. Ptracer::Ptracer(bool can_log)
  457. : is_64_bit_(false), can_log_(can_log), initialized_() {}
  458. Ptracer::Ptracer(bool is_64_bit, bool can_log)
  459. : is_64_bit_(is_64_bit), can_log_(can_log), initialized_() {
  460. INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
  461. INITIALIZATION_STATE_SET_VALID(initialized_);
  462. }
  463. Ptracer::~Ptracer() {}
  464. bool Ptracer::Initialize(pid_t pid) {
  465. INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
  466. ThreadContext context;
  467. size_t length = GetGeneralPurposeRegistersAndLength(pid, &context, can_log_);
  468. if (length == sizeof(context.t64)) {
  469. is_64_bit_ = true;
  470. } else if (length == sizeof(context.t32)) {
  471. is_64_bit_ = false;
  472. } else {
  473. LOG_IF(ERROR, can_log_)
  474. << "Unexpected registers size " << length
  475. << " != " << sizeof(context.t64) << ", " << sizeof(context.t32);
  476. return false;
  477. }
  478. INITIALIZATION_STATE_SET_VALID(initialized_);
  479. return true;
  480. }
  481. bool Ptracer::Is64Bit() {
  482. INITIALIZATION_STATE_DCHECK_VALID(initialized_);
  483. return is_64_bit_;
  484. }
  485. bool Ptracer::GetThreadInfo(pid_t tid, ThreadInfo* info) {
  486. INITIALIZATION_STATE_DCHECK_VALID(initialized_);
  487. if (is_64_bit_) {
  488. return GetGeneralPurposeRegisters64(tid, &info->thread_context, can_log_) &&
  489. GetFloatingPointRegisters64(tid, &info->float_context, can_log_) &&
  490. GetThreadArea64(tid,
  491. info->thread_context,
  492. &info->thread_specific_data_address,
  493. can_log_);
  494. }
  495. return GetGeneralPurposeRegisters32(tid, &info->thread_context, can_log_) &&
  496. GetFloatingPointRegisters32(tid, &info->float_context, can_log_) &&
  497. GetThreadArea32(tid,
  498. info->thread_context,
  499. &info->thread_specific_data_address,
  500. can_log_);
  501. }
  502. ssize_t Ptracer::ReadUpTo(pid_t pid,
  503. LinuxVMAddress address,
  504. size_t size,
  505. char* buffer) {
  506. size_t bytes_read = 0;
  507. while (size > 0) {
  508. errno = 0;
  509. if (size >= sizeof(long)) {
  510. *reinterpret_cast<long*>(buffer) =
  511. ptrace(PTRACE_PEEKDATA, pid, address, nullptr);
  512. if (errno == EIO) {
  513. ssize_t last_bytes = ReadLastBytes(pid, address, size, buffer);
  514. return last_bytes >= 0 ? bytes_read + last_bytes : -1;
  515. }
  516. if (errno != 0) {
  517. PLOG_IF(ERROR, can_log_) << "ptrace";
  518. return -1;
  519. }
  520. size -= sizeof(long);
  521. buffer += sizeof(long);
  522. address += sizeof(long);
  523. bytes_read += sizeof(long);
  524. } else {
  525. long word = ptrace(PTRACE_PEEKDATA, pid, address, nullptr);
  526. if (errno == 0) {
  527. memcpy(buffer, reinterpret_cast<char*>(&word), size);
  528. return bytes_read + size;
  529. }
  530. if (errno == EIO) {
  531. ssize_t last_bytes = ReadLastBytes(pid, address, size, buffer);
  532. return last_bytes >= 0 ? bytes_read + last_bytes : -1;
  533. }
  534. PLOG_IF(ERROR, can_log_);
  535. return -1;
  536. }
  537. }
  538. return bytes_read;
  539. }
  540. // Handles an EIO by reading at most size bytes from address into buffer if
  541. // address was within a word of a possible page boundary, by aligning to read
  542. // the last word of the page and extracting the desired bytes.
  543. ssize_t Ptracer::ReadLastBytes(pid_t pid,
  544. LinuxVMAddress address,
  545. size_t size,
  546. char* buffer) {
  547. LinuxVMAddress aligned = ((address + 4095) & ~4095) - sizeof(long);
  548. if (aligned >= address || aligned == address - sizeof(long)) {
  549. PLOG_IF(ERROR, can_log_) << "ptrace";
  550. return -1;
  551. }
  552. DCHECK_GT(aligned, address - sizeof(long));
  553. errno = 0;
  554. long word = ptrace(PTRACE_PEEKDATA, pid, aligned, nullptr);
  555. if (errno != 0) {
  556. PLOG_IF(ERROR, can_log_) << "ptrace";
  557. return -1;
  558. }
  559. size_t bytes_read = address - aligned;
  560. size_t last_bytes = std::min(sizeof(long) - bytes_read, size);
  561. memcpy(buffer, reinterpret_cast<char*>(&word) + bytes_read, last_bytes);
  562. return last_bytes;
  563. }
  564. } // namespace crashpad