104 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			104 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2018 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| 
 | |
| #include <atomic>
 | |
| #include <memory>
 | |
| 
 | |
| #include <jni.h>
 | |
| #include <signal.h>
 | |
| #include <stdint.h>
 | |
| #include <sys/mman.h>
 | |
| 
 | |
| #include "base/globals.h"
 | |
| #include "base/mem_map.h"
 | |
| #include "fault_handler.h"
 | |
| 
 | |
| namespace art {
 | |
| 
 | |
| class TestFaultHandler final : public FaultHandler {
 | |
|  public:
 | |
|   explicit TestFaultHandler(FaultManager* manager)
 | |
|       : FaultHandler(manager),
 | |
|         map_error_(),
 | |
|         target_map_(MemMap::MapAnonymous("test-305-mmap",
 | |
|                                          /* addr */ nullptr,
 | |
|                                          /* byte_count */ kPageSize,
 | |
|                                          /* prot */ PROT_NONE,
 | |
|                                          /* low_4gb */ false,
 | |
|                                          /* reuse */ false,
 | |
|                                          /* reservation */ nullptr,
 | |
|                                          /* error_msg */ &map_error_,
 | |
|                                          /* use_ashmem */ false)),
 | |
|         was_hit_(false) {
 | |
|     CHECK(target_map_.IsValid()) << "Unable to create segfault target address " << map_error_;
 | |
|     manager_->AddHandler(this, /*in_generated_code*/false);
 | |
|   }
 | |
| 
 | |
|   virtual ~TestFaultHandler() {
 | |
|     manager_->RemoveHandler(this);
 | |
|   }
 | |
| 
 | |
|   bool Action(int sig, siginfo_t* siginfo, void* context ATTRIBUTE_UNUSED) override {
 | |
|     CHECK_EQ(sig, SIGSEGV);
 | |
|     CHECK_EQ(reinterpret_cast<uint32_t*>(siginfo->si_addr),
 | |
|              GetTargetPointer()) << "Segfault on unexpected address!";
 | |
|     CHECK(!was_hit_) << "Recursive signal!";
 | |
|     was_hit_ = true;
 | |
| 
 | |
|     LOG(INFO) << "SEGV Caught. mprotecting map.";
 | |
|     CHECK(target_map_.Protect(PROT_READ | PROT_WRITE)) << "Failed to mprotect R/W";
 | |
|     LOG(INFO) << "Setting value to be read.";
 | |
|     *GetTargetPointer() = kDataValue;
 | |
|     LOG(INFO) << "Changing prot to be read-only.";
 | |
|     CHECK(target_map_.Protect(PROT_READ)) << "Failed to mprotect R-only";
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   void CauseSegfault() {
 | |
|     CHECK_EQ(target_map_.GetProtect(), PROT_NONE);
 | |
| 
 | |
|     // This will segfault. The handler should deal with it though and we will get a value out of it.
 | |
|     uint32_t data = *GetTargetPointer();
 | |
| 
 | |
|     // Prevent re-ordering around the *GetTargetPointer by the compiler
 | |
|     std::atomic_signal_fence(std::memory_order_seq_cst);
 | |
| 
 | |
|     CHECK(was_hit_);
 | |
|     CHECK_EQ(data, kDataValue) << "Unexpected read value from mmap";
 | |
|     CHECK_EQ(target_map_.GetProtect(), PROT_READ);
 | |
|     LOG(INFO) << "Success!";
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   uint32_t* GetTargetPointer() {
 | |
|     return reinterpret_cast<uint32_t*>(target_map_.Begin() + 8);
 | |
|   }
 | |
| 
 | |
|   static constexpr uint32_t kDataValue = 0xDEADBEEF;
 | |
| 
 | |
|   std::string map_error_;
 | |
|   MemMap target_map_;
 | |
|   bool was_hit_;
 | |
| };
 | |
| 
 | |
| extern "C" JNIEXPORT void JNICALL Java_Main_runFaultHandlerTest(JNIEnv*, jclass) {
 | |
|   std::unique_ptr<TestFaultHandler> handler(new TestFaultHandler(&fault_manager));
 | |
|   handler->CauseSegfault();
 | |
| }
 | |
| 
 | |
| }  // namespace art
 |