Сбой функции Google Breakpad ThreadEntry на архитектуре MIPS - PullRequest
3 голосов
/ 03 апреля 2019

Я загрузил исходный код Google breakpad, чтобы провести некоторое тестирование, демоверсия может нормально работать на X64 и ARM64, но на MIPS64 она не генерирует файл мини-дампов, и дочерний поток будет зависать после sys_clone в ExceptionHandler :: GenerateDump (); Я проверил входной адрес дочернего потока, нашел, что адрес уменьшен на 16 байт, моя ревизия ядра:

[root@neo7 breakpad-master]# uname -r
3.10.0-514.26.2.ns7.030.mips64el

Мой тестовый код:

#include "../src/client/linux/handler/exception_handler.h"
#include <pthread.h>
#include <iostream>
using namespace std;

static bool dumpCallback(const google_breakpad::MinidumpDescriptor &descriptor,
                     void *context,
                     bool succeeded)
{
   printf("Dump path: %s\n", descriptor.path());
    char cmd[512] = {0};
    snprintf(cmd, sizeof(cmd), "echo %s > dump_name", descriptor.path());
    system(cmd);
    return succeeded;
}

void *TestThread(void* arg)
{
    int input = *(int*)arg;
    int *a = (int *)(NULL);
    *a = 1;
}

int main(int argc, char *argv[])
{
    google_breakpad::MinidumpDescriptor descriptor("/tmp");
    google_breakpad::ExceptionHandler eh(descriptor,
                     NULL,
                     dumpCallback,
                     NULL,
                     true,
                     -1);
    //crashHare();
    pthread_t threadId;
    int input = 12;
    int ret = pthread_create(&threadId, NULL, TestThread, (void*)&input);
    if(ret != 0)
    {
        cout<< "create thread error"<<endl;
    }

    cout<<"main thread running"<<endl;
    pthread_join(threadId,NULL);
    return 0;
}

Я добавляю некоторые printf в ./src/client/linux/handler/exception_handler.cc, до sys_clone и после ThreadEntry, журнал выглядит так:

struct ThreadArgument {
  pid_t pid;  // the crashing process
  const MinidumpDescriptor* minidump_descriptor;
  ExceptionHandler* handler;
  const void* context;  // a CrashContext structure
  size_t context_size;
};
ThreadArgument thread_arg;
thread_arg.handler = this;
thread_arg.minidump_descriptor = &minidump_descriptor_;
thread_arg.pid = getpid();
thread_arg.context = context;
thread_arg.context_size = sizeof(*context);

zzzzzzz &thread_arg:f10ee460  (before sys_clone)
const pid_t child = sys_clone(
  ThreadEntry, stack, CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL,
  NULL);
zzzzzzz child 23889

int ExceptionHandler::ThreadEntry(void *arg) {
zzzzzzz ThreadEntry 
zzzzzzz arg:f10ee450  (child thread)

если тип thread_arg имеет тип int *, проблем со смещением адресов нет. Если добавить префикс static для thread_arg, проблема смещения адреса также не возникает.

static ThreadArgument thread_arg;  /* The same goes for malloc, but malloc is not safe */
zzzzzzz &thread_arg:20030060  (before sys_clone)
const pid_t child = sys_clone(
  ThreadEntry, stack, CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL,
  NULL);
zzzzzzz child 22399

int ExceptionHandler::ThreadEntry(void *arg) {
zzzzzzz ThreadEntry 
zzzzzzz arg:20030060  (child thread)

И я пишу другой код для проверки sys_clone под функцией обработки сигналов:

#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>

#include <sys/signal.h>
#include <sys/ucontext.h>
#include <sys/user.h>
#include <ucontext.h>

#include <algorithm>
#include <utility>
#include <vector>

#include "src/third_party/lss/linux_syscall_support.h"
#include "src/common/memory_allocator.h"
using namespace google_breakpad;
unsigned char *stack = NULL;

void my_memset(void* ip, char c, size_t len) {
  char* p = (char *) ip;
  while (len--)
    *p++ = c;
}

struct ThreadArgument {
  pid_t pid;  // the crashing process
  const void* minidump_descriptor;
  void* handler;
  const void* context;  // a CrashContext structure
  size_t context_size;
};

int ThreadEntry(void *arg) {
  printf("arg:%x\n", arg);
  const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
  printf("thread_arg:%x\n", thread_arg);
  return 0;
}
void GenerateDump(int signo) {
  static const unsigned kChildStackSize = 16000;
  PageAllocator allocator;
  uint8_t* stack = reinterpret_cast<uint8_t*>(allocator.Alloc(kChildStackSize));
  if (!stack)
    return ;
  stack += kChildStackSize;
  my_memset(stack - 16, 0, 16);

  ThreadArgument thread_arg;
  thread_arg.handler = (void *) NULL;
  thread_arg.minidump_descriptor = (void *) NULL;
  thread_arg.pid = getpid();
  thread_arg.context = (void *) NULL;
  thread_arg.context_size = sizeof(void *);
  printf("thread_arg:%x\n", &thread_arg);
  const pid_t child = sys_clone(
  ThreadEntry, stack, CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL,
  NULL);
  printf("child:%d\n", child);
  sleep(1);
  return ;
}

int main(void)
{
    signal(SIGSEGV, GenerateDump);
    int *a = (int *)NULL;
    *a = 1;

    return 0;
}

Работает нормально:

thread_arg:ffcb9030
child:8447
arg:ffcb9030
thread_arg:ffcb9030

Интересно, почему передача содержимого стека в качестве параметров вызывает это проблема несоответствия адресов в панели разбивки.

...