Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  moz_zucchini.cc

  Sprache: C
 

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/moz_zucchini.h"

#include "absl/types/optional.h"
#include "base/logging.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "build/build_config.h"
#include "components/zucchini/crc32.h"
#include "components/zucchini/mapped_file.h"
#include "components/zucchini/patch_reader.h"
#include "components/zucchini/zucchini.h"
#include "components/zucchini/zucchini_apply.h"

#include <cstdio>
#include <limits>
#include <new>
#include <string>

#if BUILDFLAG(IS_WIN)
// Get EXCEPTION_* constants defined with DWORD type
#  include <ntstatus.h>

#  include "components/zucchini/exception_filter_helper_win.h"

#  include <io.h>
#endif  // BUILDFLAG(IS_WIN)

namespace zucchini::mozilla {

#ifdef ENABLE_TESTS
// Helpers for crash recovery tests.

static TestOptions gTestOptions;

void SetTestOptions(const TestOptions& aTestOptions) {
  gTestOptions = aTestOptions;
}

// Test-only log to prove stack unwinding reaches local destructors when we
// recover from a fault originating from inside zucchini.
class ScopedDestructorMarker {
 public:
  ScopedDestructorMarker() = default;

  ~ScopedDestructorMarker() {
    if (!gTestOptions.logDestructorMarker) {
      return;
    }

    LOG(ERROR) << "MOZ_TEST_ZUCCHINI_DTOR_MARKER";
  }
};

// Test-only OOM injection used by updater tests to verify that a thrown
// bad_alloc is translated into kStatusOutOfMemory.
static void MaybeTriggerTestBadAlloc() {
  if (!gTestOptions.triggerBadAlloc) {
    return;
  }

  // This allocation is expected to throw std::bad_alloc. volatile prevents
  // the compiler from optimizing it away. If it somehow succeeded, CHECK(false)
  // ensures the test fails visibly rather than silently passing.
  voidvolatile allocation = ::operator new(std::numeric_limits<size_t>::max());
  (void)allocation;
  CHECK(false);
}

// Test-only CHECK() injection used by updater tests to verify that deliberate
// crash paths are converted into kStatusFatal.
static void MaybeTriggerTestCheckFailure() {
  if (!gTestOptions.triggerCheckFailure) {
    return;
  }

  CHECK(false);
}
#endif  // ENABLE_TESTS

static LogFunctionPtr gLogFunction = nullptr;

bool LogMessageHandler(int aSeverity, const char* aFile, int aLine,
                       size_t aMessageStart, const std::string& aStr) {
  if (!gLogFunction) {
    return false;
  }
  gLogFunction(aStr.c_str());
  return true;
}

void SetLogFunction(LogFunctionPtr aLogFunction) {
  gLogFunction = aLogFunction;
  logging::SetLogMessageHandler(LogMessageHandler);
}

uint32_t ComputeCrc32(const uint8_t* aBuf, size_t aBufSize) {
  return CalculateCrc32(aBuf, aBuf + aBufSize);
}

// This is zucchini::ApplyBuffer, except that we *assume* that aCheckedOldImage
// has the correct size and crc32 instead of checking it.
status::Code ApplyBufferUnsafe(ConstBufferView aCheckedOldImage,
                               const EnsemblePatchReader& aPatchReader,
                               MutableBufferView aNewImage) {
  for (const auto& elementPatch : aPatchReader.elements()) {
    ElementMatch match = elementPatch.element_match();
    if (!ApplyElement(match.exe_type(),
                      aCheckedOldImage[match.old_element.region()],
                      elementPatch, aNewImage[match.new_element.region()]))
      return status::kStatusFatal;
  }

  if (!aPatchReader.CheckNewFile(ConstBufferView(aNewImage))) {
    LOG(ERROR) << "Invalid aNewImage.";
    return status::kStatusInvalidNewImage;
  }
  return status::kStatusSuccess;
}

class MappedPatchImpl {
 public:
  MappedPatchImpl() = default;
  ~MappedPatchImpl() = default;
  std::optional<MappedFileReader> mFileReader;
  EnsemblePatchReader mPatchReader;
#if BUILDFLAG(IS_WIN)
  ExceptionFilterHelper mExceptionFilterHelper;
  DWORD mLastExceptionCode = 0;
#endif  // BUILDFLAG(IS_WIN)
};

MappedPatch::MappedPatch() : mImpl(new MappedPatchImpl()) {}

MappedPatch::~MappedPatch() { delete mImpl; }

#if BUILDFLAG(IS_WIN)
#  if !defined(HAVE_SEH_EXCEPTIONS) || !HAVE_SEH_EXCEPTIONS
#    error Compiler support for SEH is required to build zucchini on Windows.
#  endif

static constexpr DWORD kMsvcCppExceptionCode = 0xE06D7363;

// SEH filter for exceptions raised within zucchini code. Without this,
// recoverable exceptions would crash the updater before it can write
// update.status, causing SERVICE_STILL_APPLYING_ON_FAILURE errors.
//
// We only catch exceptions where the process state is known to be sound:
// - EXCEPTION_IN_PAGE_ERROR on mapped ranges (I/O failure, process healthy)
// - EXCEPTION_BREAKPOINT/EXCEPTION_ILLEGAL_INSTRUCTION (deliberate crash from
//   Chromium CHECK via ImmediateCrash, data validation failure)
// - 0xE06D7363 (MSVC C++ exception: std::bad_alloc from non-fallible
//   allocations in zucchini's disassembler. Neither zucchini nor its base shim
//   contain any explicit throw, so bad_alloc is the only possible C++ exception
//   observed here. On Windows, operator new allocation failure still surfaces
//   through SEH with this exception code.)
//
// Exceptions indicating corrupt process state (EXCEPTION_ACCESS_VIOLATION,
// EXCEPTION_STACK_OVERFLOW, etc.) are left unhandled so the process crashes
// as expected.
static int FilterZucchiniException(
    EXCEPTION_RECORD* aExceptionRecord,
    ExceptionFilterHelper& aPageErrorHelper,
    DWORD& aOutExceptionCode) {
  aOutExceptionCode = aExceptionRecord->ExceptionCode;

  // Check if this is a page error on our mapped ranges. This populates
  // nt_status/is_write for the handler to use in its diagnostic message.
  int pageResult = aPageErrorHelper.FilterPageError(aExceptionRecord);
  if (pageResult == EXCEPTION_EXECUTE_HANDLER) {
    return EXCEPTION_EXECUTE_HANDLER;
  }

  if (aOutExceptionCode == EXCEPTION_BREAKPOINT ||
      aOutExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION ||
      aOutExceptionCode ==
          kMsvcCppExceptionCode /* MSVC C++ exception (std::bad_alloc) */) {
    return EXCEPTION_EXECUTE_HANDLER;
  }

  return EXCEPTION_CONTINUE_SEARCH;
}

#  define BEGIN_TRY_EXCEPT() __try {
#  define END_TRY_EXCEPT()                                                 \
    }                                                                      \
    __except (FilterZucchiniException(                                     \
        GetExceptionInformation()->ExceptionRecord,                        \
        mImpl->mExceptionFilterHelper, mImpl->mLastExceptionCode)) {       \
      if (mImpl->mLastExceptionCode == EXCEPTION_IN_PAGE_ERROR) {          \
        LOG(ERROR) << "EXCEPTION_IN_PAGE_ERROR while "                     \
                   << (mImpl->mExceptionFilterHelper.is_write()            \
                           ? "writing to"                                  \
                           : "reading from")                               \
                   << " mapped files; NTSTATUS = "                         \
                   << mImpl->mExceptionFilterHelper.nt_status();           \
        return mImpl->mExceptionFilterHelper.nt_status() ==                \
                       STATUS_DISK_FULL                                    \
                   ? status::kStatusDiskFull                               \
                   : status::kStatusIoError;                               \
      }                                                                    \
      if (mImpl->mLastExceptionCode == kMsvcCppExceptionCode) {            \
        return status::kStatusOutOfMemory;                                 \
      }                                                                    \
      LOG(ERROR) << "CHECK failure (exception 0x" << std::hex              \
                 << mImpl->mLastExceptionCode                              \
                 << ") caught in zucchini; this is a bug.";                \
      return status::kStatusFatal;                                         \
    }
#else
#  define BEGIN_TRY_EXCEPT()
#  define END_TRY_EXCEPT()
#endif  // BUILDFLAG(IS_WIN)

// This corresponds to the first half of zucchini::ApplyCommon.
status::Code MappedPatch::Load(FILE* aPatchFile, uint32_t* aSourceSize,
                               uint32_t* aDestinationSize,
                               uint32_t* aSourceCrc32) {
  base::File patchFile = base::FILEToFile(aPatchFile);
  if (!patchFile.IsValid()) {
    LOG(ERROR) << "Invalid patch file.";
    return status::kStatusFileReadError;
  }
  mImpl->mFileReader.emplace(std::move(patchFile));
  auto& fileReader = *mImpl->mFileReader;
  if (fileReader.HasError()) {
    LOG(ERROR) << "Error with patch file: " << fileReader.error();
    if (fileReader.error_is_oom()) {
      return status::kStatusOutOfMemory;
    }
    return status::kStatusFileReadError;
  }
#if BUILDFLAG(IS_WIN)
  mImpl->mExceptionFilterHelper.AddRange(
      {fileReader.data(), fileReader.length()});
#endif
  BEGIN_TRY_EXCEPT()
#ifdef ENABLE_TESTS
  ScopedDestructorMarker destructorTester;
  MaybeTriggerTestBadAlloc();
  MaybeTriggerTestCheckFailure();
#endif  // ENABLE_TESTS

  BufferSource source(fileReader.region());
  auto& patchReader = mImpl->mPatchReader;
  if (!patchReader.Initialize(&source)) {
    LOG(ERROR) << "Error reading patch header.";
    return status::kStatusPatchReadError;
  }
  auto& header = patchReader.header();
  *aSourceSize = header.old_size;
  *aDestinationSize = header.new_size;
  *aSourceCrc32 = header.old_crc;
  return status::kStatusSuccess;
  END_TRY_EXCEPT()
}

// This corresponds to the second half of zucchini::ApplyCommon.
status::Code MappedPatch::ApplyUnsafe(const uint8_t* aCheckedOldImage,
                                      size_t aCheckedOldImageSize,
                                      FILE* aNewFile) {
  ConstBufferView oldImageView(aCheckedOldImage, aCheckedOldImageSize);

  base::File newFile = base::FILEToFile(aNewFile);
  if (!newFile.IsValid()) {
    LOG(ERROR) << "Invalid new file.";
    return status::kStatusFileWriteError;
  }

  BEGIN_TRY_EXCEPT()
  PatchHeader header = mImpl->mPatchReader.header();
  base::FilePath name;
  name = name.AppendASCII("old_name");
  MappedFileWriter mappedNew(name, std::move(newFile), header.new_size,
                             /*keep*/ true);
  if (mappedNew.HasError()) {
    LOG(ERROR) << "Error with new file: " << mappedNew.error();
    if (mappedNew.error_is_oom()) {
      return status::kStatusOutOfMemory;
    }
    return status::kStatusFileWriteError;
  }

#if BUILDFLAG(IS_WIN)
  mImpl->mExceptionFilterHelper.AddRange(
      {mappedNew.data(), mappedNew.length()});
#endif

  status::Code result =
      ApplyBufferUnsafe(oldImageView, mImpl->mPatchReader, mappedNew.region());
  if (result != status::kStatusSuccess) {
    LOG(ERROR) << "Fatal error encountered while applying patch.";
    return result;
  }

  if (!mappedNew.Flush()) {
    LOG(ERROR) << "Error flushing changes to disk: " << mappedNew.error();
    return status::kStatusFileWriteError;
  }

  return status::kStatusSuccess;
  END_TRY_EXCEPT()
}

}  // namespace zucchini::mozilla

Messung V0.5 in Prozent
C=89 H=95 G=91

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142