vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / debug / BreakpointManager.cpp
blobf105a4040a80957b35e2d92f9a4b70f386eda5bb
1 /*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include "BreakpointManager.h"
8 #include <algorithm>
10 #include <AutoDeleter.h>
12 #include <kernel.h>
13 #include <util/AutoLock.h>
14 #include <vm/vm.h>
17 //#define TRACE_BREAKPOINT_MANAGER
18 #ifdef TRACE_BREAKPOINT_MANAGER
19 # define TRACE(x...) dprintf(x)
20 #else
21 # define TRACE(x...) do {} while (false)
22 #endif
25 // soft limit for the number of breakpoints
26 const int32 kMaxBreakpointCount = 10240;
29 BreakpointManager::InstalledBreakpoint::InstalledBreakpoint(addr_t address)
31 breakpoint(NULL),
32 address(address)
37 // #pragma mark -
40 BreakpointManager::BreakpointManager()
42 fBreakpointCount(0),
43 fWatchpointCount(0)
45 rw_lock_init(&fLock, "breakpoint manager");
49 BreakpointManager::~BreakpointManager()
51 WriteLocker locker(fLock);
53 // delete the installed breakpoint objects
54 BreakpointTree::Iterator it = fBreakpoints.GetIterator();
55 while (InstalledBreakpoint* installedBreakpoint = it.Next()) {
56 it.Remove();
58 // delete underlying software breakpoint
59 if (installedBreakpoint->breakpoint->software)
60 delete installedBreakpoint->breakpoint;
62 delete installedBreakpoint;
65 // delete the watchpoints
66 while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead())
67 delete watchpoint;
69 // delete the hardware breakpoint objects
70 while (Breakpoint* breakpoint = fHardwareBreakpoints.RemoveHead())
71 delete breakpoint;
73 rw_lock_destroy(&fLock);
77 status_t
78 BreakpointManager::Init()
80 // create objects for the hardware breakpoints
81 for (int32 i = 0; i < DEBUG_MAX_BREAKPOINTS; i++) {
82 Breakpoint* breakpoint = new(std::nothrow) Breakpoint;
83 if (breakpoint == NULL)
84 return B_NO_MEMORY;
86 breakpoint->address = 0;
87 breakpoint->installedBreakpoint = NULL;
88 breakpoint->used = false;
89 breakpoint->software = false;
91 fHardwareBreakpoints.Add(breakpoint);
94 return B_OK;
98 status_t
99 BreakpointManager::InstallBreakpoint(void* _address)
101 const addr_t address = (addr_t)_address;
103 WriteLocker locker(fLock);
105 if (fBreakpointCount >= kMaxBreakpointCount)
106 return B_BUSY;
108 // check whether there's already a breakpoint at the address
109 InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
110 if (installed != NULL)
111 return B_BAD_VALUE;
113 // create the breakpoint object
114 installed = new(std::nothrow) InstalledBreakpoint(address);
115 if (installed == NULL)
116 return B_NO_MEMORY;
117 ObjectDeleter<InstalledBreakpoint> installedDeleter(installed);
119 // If we still have enough hardware breakpoints left, install a hardware
120 // breakpoint.
121 Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(false);
122 if (breakpoint != NULL) {
123 status_t error = _InstallHardwareBreakpoint(breakpoint, address);
124 if (error != B_OK)
125 return error;
127 breakpoint->installedBreakpoint = installed;
128 installed->breakpoint = breakpoint;
129 } else {
130 // install a software breakpoint
131 status_t error = _InstallSoftwareBreakpoint(installed, address);
132 if (error != B_OK)
133 return error;
136 fBreakpoints.Insert(installed);
137 installedDeleter.Detach();
138 fBreakpointCount++;
140 return B_OK;
144 status_t
145 BreakpointManager::UninstallBreakpoint(void* _address)
147 const addr_t address = (addr_t)_address;
149 WriteLocker locker(fLock);
151 InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
152 if (installed == NULL)
153 return B_BAD_VALUE;
155 if (installed->breakpoint->software)
156 _UninstallSoftwareBreakpoint(installed->breakpoint);
157 else
158 _UninstallHardwareBreakpoint(installed->breakpoint);
160 fBreakpoints.Remove(installed);
161 delete installed;
162 fBreakpointCount--;
164 return B_OK;
168 status_t
169 BreakpointManager::InstallWatchpoint(void* _address, uint32 type, int32 length)
171 const addr_t address = (addr_t)_address;
173 WriteLocker locker(fLock);
175 InstalledWatchpoint* watchpoint = _FindWatchpoint(address);
176 if (watchpoint != NULL)
177 return B_BAD_VALUE;
179 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
180 // We need at least one hardware breakpoint for our breakpoint management.
181 if (fWatchpointCount + 1 >= DEBUG_MAX_WATCHPOINTS)
182 return B_BUSY;
183 #else
184 if (fWatchpointCount >= DEBUG_MAX_WATCHPOINTS)
185 return B_BUSY;
186 #endif
188 watchpoint = new(std::nothrow) InstalledWatchpoint;
189 if (watchpoint == NULL)
190 return B_NO_MEMORY;
191 ObjectDeleter<InstalledWatchpoint> watchpointDeleter(watchpoint);
193 status_t error = _InstallWatchpoint(watchpoint, address, type, length);
194 if (error != B_OK)
195 return error;
197 fWatchpoints.Add(watchpointDeleter.Detach());
198 fWatchpointCount++;
199 return B_OK;
203 status_t
204 BreakpointManager::UninstallWatchpoint(void* address)
206 WriteLocker locker(fLock);
208 InstalledWatchpoint* watchpoint = _FindWatchpoint((addr_t)address);
209 if (watchpoint == NULL)
210 return B_BAD_VALUE;
212 ObjectDeleter<InstalledWatchpoint> deleter(watchpoint);
213 fWatchpoints.Remove(watchpoint);
214 fWatchpointCount--;
216 return _UninstallWatchpoint(watchpoint);
220 void
221 BreakpointManager::RemoveAllBreakpoints()
223 WriteLocker locker(fLock);
225 // remove the breakpoints
226 BreakpointTree::Iterator it = fBreakpoints.GetIterator();
227 while (InstalledBreakpoint* installedBreakpoint = it.Next()) {
228 it.Remove();
230 // uninstall underlying hard/software breakpoint
231 if (installedBreakpoint->breakpoint->software)
232 _UninstallSoftwareBreakpoint(installedBreakpoint->breakpoint);
233 else
234 _UninstallHardwareBreakpoint(installedBreakpoint->breakpoint);
236 delete installedBreakpoint;
239 // remove the watchpoints
240 while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead()) {
241 _UninstallWatchpoint(watchpoint);
242 delete watchpoint;
247 /*! \brief Returns whether the given address can be accessed in principle.
248 No check whether there's an actually accessible area is performed, though.
250 /*static*/ bool
251 BreakpointManager::CanAccessAddress(const void* _address, bool write)
253 const addr_t address = (addr_t)_address;
255 // user addresses are always fine
256 if (IS_USER_ADDRESS(address))
257 return true;
259 return false;
263 /*! \brief Reads data from user memory.
265 Tries to read \a size bytes of data from user memory address \a address
266 into the supplied buffer \a buffer. If only a part could be read the
267 function won't fail. The number of bytes actually read is return through
268 \a bytesRead.
270 \param address The user memory address from which to read.
271 \param buffer The buffer into which to write.
272 \param size The number of bytes to read.
273 \param bytesRead Will be set to the number of bytes actually read.
274 \return \c B_OK, if reading went fine. Then \a bytesRead will be set to
275 the amount of data actually read. An error indicates that nothing
276 has been read.
278 status_t
279 BreakpointManager::ReadMemory(const void* _address, void* buffer, size_t size,
280 size_t& bytesRead)
282 const addr_t address = (addr_t)_address;
284 ReadLocker locker(fLock);
286 status_t error = _ReadMemory(address, buffer, size, bytesRead);
287 if (error != B_OK)
288 return error;
290 // If we have software breakpoints installed, fix the buffer not to contain
291 // any of them.
293 // address of the first possibly intersecting software breakpoint
294 const addr_t startAddress
295 = std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1)
296 - (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1);
298 BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true,
299 true);
300 while (InstalledBreakpoint* installed = it.Next()) {
301 Breakpoint* breakpoint = installed->breakpoint;
302 if (breakpoint->address >= address + size)
303 break;
305 if (breakpoint->software) {
306 // Software breakpoint intersects -- replace the read data with
307 // the data saved in the breakpoint object.
308 addr_t minAddress = std::max(breakpoint->address, address);
309 size_t toCopy = std::min(address + size,
310 breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE)
311 - minAddress;
312 memcpy((uint8*)buffer + (minAddress - address),
313 breakpoint->softwareData + (minAddress - breakpoint->address),
314 toCopy);
318 return B_OK;
322 status_t
323 BreakpointManager::WriteMemory(void* _address, const void* _buffer, size_t size,
324 size_t& bytesWritten)
326 bytesWritten = 0;
328 if (size == 0)
329 return B_OK;
331 addr_t address = (addr_t)_address;
332 const uint8* buffer = (uint8*)_buffer;
334 WriteLocker locker(fLock);
336 // We don't want to overwrite software breakpoints, so things are a bit more
337 // complicated. We iterate through the intersecting software breakpoints,
338 // writing the memory between them normally, but skipping the breakpoints
339 // itself. We write into their softwareData instead.
341 // Get the first breakpoint -- if it starts before the address, we'll
342 // handle it separately to make things in the main loop simpler.
343 const addr_t startAddress
344 = std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1)
345 - (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1);
347 BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true,
348 true);
349 InstalledBreakpoint* installed = it.Next();
350 while (installed != NULL) {
351 Breakpoint* breakpoint = installed->breakpoint;
352 if (breakpoint->address >= address)
353 break;
355 if (breakpoint->software) {
356 // We've got a breakpoint that is partially intersecting with the
357 // beginning of the address range to write.
358 size_t toCopy = std::min(address + size,
359 breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE)
360 - address;
361 memcpy(breakpoint->softwareData + (address - breakpoint->address),
362 buffer, toCopy);
364 address += toCopy;
365 size -= toCopy;
366 bytesWritten += toCopy;
367 buffer += toCopy;
370 installed = it.Next();
373 // loop through the breakpoints intersecting with the range
374 while (installed != NULL) {
375 Breakpoint* breakpoint = installed->breakpoint;
376 if (breakpoint->address >= address + size)
377 break;
379 if (breakpoint->software) {
380 // write the data up to the breakpoint (if any)
381 size_t toCopy = breakpoint->address - address;
382 if (toCopy > 0) {
383 size_t chunkWritten;
384 status_t error = _WriteMemory(address, buffer, toCopy,
385 chunkWritten);
386 if (error != B_OK)
387 return bytesWritten > 0 ? B_OK : error;
389 address += chunkWritten;
390 size -= chunkWritten;
391 bytesWritten += chunkWritten;
392 buffer += chunkWritten;
394 if (chunkWritten < toCopy)
395 return B_OK;
398 // write to the breakpoint data
399 toCopy = std::min(size, (size_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE);
400 memcpy(breakpoint->softwareData, buffer, toCopy);
402 address += toCopy;
403 size -= toCopy;
404 bytesWritten += toCopy;
405 buffer += toCopy;
408 installed = it.Next();
411 // write remaining data
412 if (size > 0) {
413 size_t chunkWritten;
414 status_t error = _WriteMemory(address, buffer, size, chunkWritten);
415 if (error != B_OK)
416 return bytesWritten > 0 ? B_OK : error;
418 bytesWritten += chunkWritten;
421 return B_OK;
425 void
426 BreakpointManager::PrepareToContinue(void* _address)
428 const addr_t address = (addr_t)_address;
430 WriteLocker locker(fLock);
432 // Check whether there's a software breakpoint at the continuation address.
433 InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
434 if (installed == NULL || !installed->breakpoint->software)
435 return;
437 // We need to replace the software breakpoint by a hardware one, or
438 // we can't continue the thread.
439 Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(true);
440 if (breakpoint == NULL) {
441 dprintf("Failed to allocate a hardware breakpoint.\n");
442 return;
445 status_t error = _InstallHardwareBreakpoint(breakpoint, address);
446 if (error != B_OK)
447 return;
449 _UninstallSoftwareBreakpoint(installed->breakpoint);
451 breakpoint->installedBreakpoint = installed;
452 installed->breakpoint = breakpoint;
456 BreakpointManager::Breakpoint*
457 BreakpointManager::_GetUnusedHardwareBreakpoint(bool force)
459 // try to find a free one first
460 for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator();
461 Breakpoint* breakpoint = it.Next();) {
462 if (!breakpoint->used)
463 return breakpoint;
466 if (!force)
467 return NULL;
469 // replace one by a software breakpoint
470 for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator();
471 Breakpoint* breakpoint = it.Next();) {
472 if (breakpoint->installedBreakpoint == NULL)
473 continue;
475 status_t error = _InstallSoftwareBreakpoint(
476 breakpoint->installedBreakpoint, breakpoint->address);
477 if (error != B_OK)
478 continue;
480 if (_UninstallHardwareBreakpoint(breakpoint) == B_OK)
481 return breakpoint;
484 return NULL;
488 status_t
489 BreakpointManager::_InstallSoftwareBreakpoint(InstalledBreakpoint* installed,
490 addr_t address)
492 Breakpoint* breakpoint = new(std::nothrow) Breakpoint;
493 if (breakpoint == NULL)
494 return B_NO_MEMORY;
495 ObjectDeleter<Breakpoint> breakpointDeleter(breakpoint);
497 breakpoint->address = address;
498 breakpoint->installedBreakpoint = installed;
499 breakpoint->used = true;
500 breakpoint->software = true;
502 // save the memory where the software breakpoint shall be installed
503 size_t bytesTransferred;
504 status_t error = _ReadMemory(address, breakpoint->softwareData,
505 DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred);
506 if (error != B_OK)
507 return error;
508 if (bytesTransferred != DEBUG_SOFTWARE_BREAKPOINT_SIZE)
509 return B_BAD_ADDRESS;
511 // write the breakpoint code
512 error = _WriteMemory(address, DEBUG_SOFTWARE_BREAKPOINT,
513 DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred);
514 if (error != B_OK)
515 return error;
517 if (bytesTransferred < DEBUG_SOFTWARE_BREAKPOINT_SIZE) {
518 // breakpoint written partially only -- undo the written part
519 if (bytesTransferred > 0) {
520 size_t dummy;
521 _WriteMemory(address, breakpoint->softwareData, bytesTransferred,
522 dummy);
524 return B_BAD_ADDRESS;
527 installed->breakpoint = breakpoint;
528 breakpointDeleter.Detach();
530 TRACE("installed software breakpoint at %#lx\n", address);
532 return B_OK;
536 status_t
537 BreakpointManager::_UninstallSoftwareBreakpoint(Breakpoint* breakpoint)
539 size_t bytesWritten;
540 _WriteMemory(breakpoint->address, breakpoint->softwareData,
541 DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesWritten);
543 TRACE("uninstalled software breakpoint at %#lx\n", breakpoint->address);
545 delete breakpoint;
546 return B_OK;
550 status_t
551 BreakpointManager::_InstallHardwareBreakpoint(Breakpoint* breakpoint,
552 addr_t address)
554 status_t error = arch_set_breakpoint((void*)address);
555 if (error != B_OK)
556 return error;
558 // move to the tail of the list
559 fHardwareBreakpoints.Remove(breakpoint);
560 fHardwareBreakpoints.Add(breakpoint);
562 TRACE("installed hardware breakpoint at %#lx\n", address);
564 breakpoint->address = address;
565 breakpoint->used = true;
566 return B_OK;
570 status_t
571 BreakpointManager::_UninstallHardwareBreakpoint(Breakpoint* breakpoint)
573 status_t error = arch_clear_breakpoint((void*)breakpoint->address);
574 if (error != B_OK)
575 return error;
577 TRACE("uninstalled hardware breakpoint at %#lx\n", breakpoint->address);
579 breakpoint->used = false;
580 breakpoint->installedBreakpoint = NULL;
581 return B_OK;
585 BreakpointManager::InstalledWatchpoint*
586 BreakpointManager::_FindWatchpoint(addr_t address) const
588 for (InstalledWatchpointList::ConstIterator it = fWatchpoints.GetIterator();
589 InstalledWatchpoint* watchpoint = it.Next();) {
590 if (address == watchpoint->address)
591 return watchpoint;
594 return NULL;
598 status_t
599 BreakpointManager::_InstallWatchpoint(InstalledWatchpoint* watchpoint,
600 addr_t address, uint32 type, int32 length)
602 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
603 // We need a hardware breakpoint.
604 watchpoint->breakpoint = _GetUnusedHardwareBreakpoint(true);
605 if (watchpoint->breakpoint == NULL) {
606 dprintf("Failed to allocate a hardware breakpoint for watchpoint.\n");
607 return B_BUSY;
609 #endif
611 status_t error = arch_set_watchpoint((void*)address, type, length);
612 if (error != B_OK)
613 return error;
615 watchpoint->address = address;
617 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
618 watchpoint->breakpoint->used = true;
619 #endif
621 return B_OK;
625 status_t
626 BreakpointManager::_UninstallWatchpoint(InstalledWatchpoint* watchpoint)
628 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
629 watchpoint->breakpoint->used = false;
630 #endif
632 return arch_clear_watchpoint((void*)watchpoint->address);
636 status_t
637 BreakpointManager::_ReadMemory(const addr_t _address, void* _buffer,
638 size_t size, size_t& bytesRead)
640 const uint8* address = (const uint8*)_address;
641 uint8* buffer = (uint8*)_buffer;
643 // check the parameters
644 if (!CanAccessAddress(address, false))
645 return B_BAD_ADDRESS;
646 if (size <= 0)
647 return B_BAD_VALUE;
649 // If the region to be read crosses page boundaries, we split it up into
650 // smaller chunks.
651 status_t error = B_OK;
652 bytesRead = 0;
653 while (size > 0) {
654 // check whether we're still in user address space
655 if (!CanAccessAddress(address, false)) {
656 error = B_BAD_ADDRESS;
657 break;
660 // don't cross page boundaries in a single read
661 int32 toRead = size;
662 int32 maxRead = B_PAGE_SIZE - (addr_t)address % B_PAGE_SIZE;
663 if (toRead > maxRead)
664 toRead = maxRead;
666 error = user_memcpy(buffer, address, toRead);
667 if (error != B_OK)
668 break;
670 bytesRead += toRead;
671 address += toRead;
672 buffer += toRead;
673 size -= toRead;
676 // If reading fails, we only fail, if we haven't read anything yet.
677 if (error != B_OK) {
678 if (bytesRead > 0)
679 return B_OK;
680 return error;
683 return B_OK;
687 status_t
688 BreakpointManager::_WriteMemory(addr_t _address, const void* _buffer,
689 size_t size, size_t& bytesWritten)
691 uint8* address = (uint8*)_address;
692 const uint8* buffer = (const uint8*)_buffer;
694 // check the parameters
695 if (!CanAccessAddress(address, true))
696 return B_BAD_ADDRESS;
697 if (size <= 0)
698 return B_BAD_VALUE;
700 // If the region to be written crosses area boundaries, we split it up into
701 // smaller chunks.
702 status_t error = B_OK;
703 bytesWritten = 0;
704 while (size > 0) {
705 // check whether we're still in user address space
706 if (!CanAccessAddress(address, true)) {
707 error = B_BAD_ADDRESS;
708 break;
711 // get the area for the address (we need to use _user_area_for(), since
712 // we're looking for a user area)
713 area_id area = _user_area_for(address);
714 if (area < 0) {
715 TRACE("BreakpointManager::_WriteMemory(): area not found for "
716 "address: %p: %lx\n", address, area);
717 error = area;
718 break;
721 area_info areaInfo;
722 status_t error = get_area_info(area, &areaInfo);
723 if (error != B_OK) {
724 TRACE("BreakpointManager::_WriteMemory(): failed to get info for "
725 "area %ld: %lx\n", area, error);
726 error = B_BAD_ADDRESS;
727 break;
730 // restrict this round of writing to the found area
731 int32 toWrite = size;
732 int32 maxWrite = (uint8*)areaInfo.address + areaInfo.size - address;
733 if (toWrite > maxWrite)
734 toWrite = maxWrite;
736 // if the area is read-only, we temporarily need to make it writable
737 bool protectionChanged = false;
738 if (!(areaInfo.protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA))) {
739 error = set_area_protection(area,
740 areaInfo.protection | B_WRITE_AREA);
741 if (error != B_OK) {
742 TRACE("BreakpointManager::_WriteMemory(): failed to set new "
743 "protection for area %ld: %lx\n", area, error);
744 break;
746 protectionChanged = true;
749 // copy the memory
750 error = user_memcpy(address, buffer, toWrite);
752 // reset the area protection
753 if (protectionChanged)
754 set_area_protection(area, areaInfo.protection);
756 if (error != B_OK) {
757 TRACE("BreakpointManager::_WriteMemory(): user_memcpy() failed: "
758 "%lx\n", error);
759 break;
762 bytesWritten += toWrite;
763 address += toWrite;
764 buffer += toWrite;
765 size -= toWrite;
768 // If writing fails, we only fail, if we haven't written anything yet.
769 if (error != B_OK) {
770 if (bytesWritten > 0)
771 return B_OK;
772 return error;
775 return B_OK;