tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FuzzingFunctions.cpp (15273B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "FuzzingFunctions.h"
      8 
      9 #include "js/GCAPI.h"
     10 #include "mozilla/ErrorResult.h"
     11 #include "mozilla/SpinEventLoopUntil.h"
     12 #include "mozilla/Sprintf.h"
     13 #include "mozilla/TextEvents.h"
     14 #include "mozilla/TextInputProcessor.h"
     15 #include "mozilla/dom/ContentChild.h"
     16 #include "mozilla/dom/KeyboardEvent.h"
     17 #include "nsFocusManager.h"
     18 #include "nsIAccessibilityService.h"
     19 #include "nsITimer.h"
     20 #include "nsJSEnvironment.h"
     21 #include "nsPIDOMWindow.h"
     22 #include "xpcAccessibilityService.h"
     23 
     24 namespace mozilla::dom {
     25 
     26 /* static */
     27 void FuzzingFunctions::GarbageCollect(const GlobalObject&) {
     28  nsJSContext::GarbageCollectNow(JS::GCReason::COMPONENT_UTILS,
     29                                 nsJSContext::NonShrinkingGC);
     30 }
     31 
     32 /* static */
     33 void FuzzingFunctions::GarbageCollectCompacting(const GlobalObject&) {
     34  nsJSContext::GarbageCollectNow(JS::GCReason::COMPONENT_UTILS,
     35                                 nsJSContext::ShrinkingGC);
     36 }
     37 
     38 /* static */
     39 void FuzzingFunctions::Crash(const GlobalObject& aGlobalObject,
     40                             const nsAString& aKeyValue) {
     41  char msgbuf[250];
     42 
     43  SprintfLiteral(msgbuf, "%s", NS_ConvertUTF16toUTF8(aKeyValue).get());
     44  if (aKeyValue.Length() >= sizeof(msgbuf)) {
     45    // Update the end of a truncated message to '...'.
     46    strcpy(&msgbuf[sizeof(msgbuf) - 4], "...");
     47  }
     48  MOZ_CRASH_UNSAFE_PRINTF("%s", msgbuf);
     49 }
     50 
     51 /* static */
     52 void FuzzingFunctions::KillGPUProcess(const GlobalObject&) {
     53  ContentChild::GetSingleton()->SendKillGPUProcess();
     54 }
     55 
     56 /* static */
     57 void FuzzingFunctions::CycleCollect(const GlobalObject&) {
     58  nsJSContext::CycleCollectNow(CCReason::API);
     59 }
     60 
     61 void FuzzingFunctions::MemoryPressure(const GlobalObject&) {
     62  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     63  os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
     64 }
     65 
     66 /* static */
     67 void FuzzingFunctions::SignalIPCReady(const GlobalObject&) {
     68 #ifdef FUZZING_SNAPSHOT
     69  ContentChild::GetSingleton()->SendSignalFuzzingReady();
     70 #endif
     71 }
     72 
     73 /* static */
     74 void FuzzingFunctions::EnableAccessibility(const GlobalObject&,
     75                                           ErrorResult& aRv) {
     76  RefPtr<nsIAccessibilityService> a11y;
     77  nsresult rv;
     78 
     79  rv = NS_GetAccessibilityService(getter_AddRefs(a11y));
     80  if (NS_FAILED(rv)) {
     81    aRv.Throw(rv);
     82  }
     83 }
     84 
     85 struct ModifierKey final {
     86  Modifier mModifier;
     87  KeyNameIndex mKeyNameIndex;
     88  bool mLockable;
     89 
     90  constexpr ModifierKey(Modifier aModifier, KeyNameIndex aKeyNameIndex,
     91                        bool aLockable)
     92      : mModifier(aModifier),
     93        mKeyNameIndex(aKeyNameIndex),
     94        mLockable(aLockable) {}
     95 };
     96 
     97 static constexpr ModifierKey kModifierKeys[] = {
     98    {MODIFIER_ALT, KEY_NAME_INDEX_Alt, false},
     99    {MODIFIER_ALTGRAPH, KEY_NAME_INDEX_AltGraph, false},
    100    {MODIFIER_CONTROL, KEY_NAME_INDEX_Control, false},
    101    {MODIFIER_FN, KEY_NAME_INDEX_Fn, false},
    102    {MODIFIER_META, KEY_NAME_INDEX_Meta, false},
    103    {MODIFIER_SHIFT, KEY_NAME_INDEX_Shift, false},
    104    {MODIFIER_SYMBOL, KEY_NAME_INDEX_Symbol, false},
    105    {MODIFIER_CAPSLOCK, KEY_NAME_INDEX_CapsLock, true},
    106    {MODIFIER_FNLOCK, KEY_NAME_INDEX_FnLock, true},
    107    {MODIFIER_NUMLOCK, KEY_NAME_INDEX_NumLock, true},
    108    {MODIFIER_SCROLLLOCK, KEY_NAME_INDEX_ScrollLock, true},
    109    {MODIFIER_SYMBOLLOCK, KEY_NAME_INDEX_SymbolLock, true},
    110 };
    111 
    112 /* static */
    113 Modifiers FuzzingFunctions::ActivateModifiers(
    114    TextInputProcessor* aTextInputProcessor, Modifiers aModifiers,
    115    nsIWidget* aWidget, ErrorResult& aRv) {
    116  MOZ_ASSERT(aTextInputProcessor);
    117 
    118  if (aModifiers == MODIFIER_NONE) {
    119    return MODIFIER_NONE;
    120  }
    121 
    122  // We don't want to dispatch modifier key event from here.  In strictly
    123  // speaking, all necessary modifiers should be activated with dispatching
    124  // each modifier key event.  However, we cannot keep storing
    125  // TextInputProcessor instance for multiple SynthesizeKeyboardEvents() calls.
    126  // So, if some callers need to emulate modifier key events, they should do
    127  // it by themselves.
    128  uint32_t flags = nsITextInputProcessor::KEY_NON_PRINTABLE_KEY |
    129                   nsITextInputProcessor::KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT;
    130 
    131  Modifiers activatedModifiers = MODIFIER_NONE;
    132  Modifiers activeModifiers = aTextInputProcessor->GetActiveModifiers();
    133  for (const ModifierKey& kModifierKey : kModifierKeys) {
    134    if (!(kModifierKey.mModifier & aModifiers)) {
    135      continue;  // Not requested modifier.
    136    }
    137    if (kModifierKey.mModifier & activeModifiers) {
    138      continue;  // Already active, do nothing.
    139    }
    140    WidgetKeyboardEvent event(true, eVoidEvent, aWidget);
    141    // mKeyCode will be computed by TextInputProcessor automatically.
    142    event.mKeyNameIndex = kModifierKey.mKeyNameIndex;
    143    aRv = aTextInputProcessor->Keydown(event, flags);
    144    if (NS_WARN_IF(aRv.Failed())) {
    145      return activatedModifiers;
    146    }
    147    if (kModifierKey.mLockable) {
    148      aRv = aTextInputProcessor->Keyup(event, flags);
    149      if (NS_WARN_IF(aRv.Failed())) {
    150        return activatedModifiers;
    151      }
    152    }
    153    activatedModifiers |= kModifierKey.mModifier;
    154  }
    155  return activatedModifiers;
    156 }
    157 
    158 /* static */
    159 Modifiers FuzzingFunctions::InactivateModifiers(
    160    TextInputProcessor* aTextInputProcessor, Modifiers aModifiers,
    161    nsIWidget* aWidget, ErrorResult& aRv) {
    162  MOZ_ASSERT(aTextInputProcessor);
    163 
    164  if (aModifiers == MODIFIER_NONE) {
    165    return MODIFIER_NONE;
    166  }
    167 
    168  // We don't want to dispatch modifier key event from here.  In strictly
    169  // speaking, all necessary modifiers should be activated with dispatching
    170  // each modifier key event.  However, we cannot keep storing
    171  // TextInputProcessor instance for multiple SynthesizeKeyboardEvents() calls.
    172  // So, if some callers need to emulate modifier key events, they should do
    173  // it by themselves.
    174  uint32_t flags = nsITextInputProcessor::KEY_NON_PRINTABLE_KEY |
    175                   nsITextInputProcessor::KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT;
    176 
    177  Modifiers inactivatedModifiers = MODIFIER_NONE;
    178  Modifiers activeModifiers = aTextInputProcessor->GetActiveModifiers();
    179  for (const ModifierKey& kModifierKey : kModifierKeys) {
    180    if (!(kModifierKey.mModifier & aModifiers)) {
    181      continue;  // Not requested modifier.
    182    }
    183    if (kModifierKey.mModifier & activeModifiers) {
    184      continue;  // Already active, do nothing.
    185    }
    186    WidgetKeyboardEvent event(true, eVoidEvent, aWidget);
    187    // mKeyCode will be computed by TextInputProcessor automatically.
    188    event.mKeyNameIndex = kModifierKey.mKeyNameIndex;
    189    if (kModifierKey.mLockable) {
    190      aRv = aTextInputProcessor->Keydown(event, flags);
    191      if (NS_WARN_IF(aRv.Failed())) {
    192        return inactivatedModifiers;
    193      }
    194    }
    195    aRv = aTextInputProcessor->Keyup(event, flags);
    196    if (NS_WARN_IF(aRv.Failed())) {
    197      return inactivatedModifiers;
    198    }
    199    inactivatedModifiers |= kModifierKey.mModifier;
    200  }
    201  return inactivatedModifiers;
    202 }
    203 
    204 /* static */
    205 void FuzzingFunctions::SynthesizeKeyboardEvents(
    206    const GlobalObject& aGlobalObject, const nsAString& aKeyValue,
    207    const KeyboardEventInit& aDict, ErrorResult& aRv) {
    208  // Prepare keyboard event to synthesize first.
    209  uint32_t flags = 0;
    210  // Don't modify the given dictionary since caller may want to modify
    211  // a part of it and call this with it again.
    212  WidgetKeyboardEvent event(true, eVoidEvent, nullptr);
    213  event.mKeyCode = aDict.mKeyCode;
    214  event.mCharCode = 0;  // Ignore.
    215  event.mKeyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aKeyValue);
    216  if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
    217    event.mKeyValue = aKeyValue;
    218  }
    219  // code value should be empty string or one of valid code value.
    220  event.mCodeNameIndex =
    221      aDict.mCode.IsEmpty()
    222          ? CODE_NAME_INDEX_UNKNOWN
    223          : WidgetKeyboardEvent::GetCodeNameIndex(aDict.mCode);
    224  if (NS_WARN_IF(event.mCodeNameIndex == CODE_NAME_INDEX_USE_STRING)) {
    225    // Meaning that the code value is specified but it's not a known code
    226    // value.  TextInputProcessor does not support synthesizing keyboard
    227    // events with unknown code value.  So, returns error now.
    228    aRv.Throw(NS_ERROR_INVALID_ARG);
    229    return;
    230  }
    231  event.mLocation = aDict.mLocation;
    232  event.mIsRepeat = aDict.mRepeat;
    233 
    234 #define SET_MODIFIER(aName, aValue) \
    235  if (aDict.m##aName) {             \
    236    event.mModifiers |= aValue;     \
    237  }
    238 
    239  SET_MODIFIER(CtrlKey, MODIFIER_CONTROL)
    240  SET_MODIFIER(ShiftKey, MODIFIER_SHIFT)
    241  SET_MODIFIER(AltKey, MODIFIER_ALT)
    242  SET_MODIFIER(MetaKey, MODIFIER_META)
    243  SET_MODIFIER(ModifierAltGraph, MODIFIER_ALTGRAPH)
    244  SET_MODIFIER(ModifierCapsLock, MODIFIER_CAPSLOCK)
    245  SET_MODIFIER(ModifierFn, MODIFIER_FN)
    246  SET_MODIFIER(ModifierFnLock, MODIFIER_FNLOCK)
    247  SET_MODIFIER(ModifierNumLock, MODIFIER_NUMLOCK)
    248  SET_MODIFIER(ModifierScrollLock, MODIFIER_SCROLLLOCK)
    249  SET_MODIFIER(ModifierSymbol, MODIFIER_SYMBOL)
    250  SET_MODIFIER(ModifierSymbolLock, MODIFIER_SYMBOLLOCK)
    251 
    252 #undef SET_MODIFIER
    253 
    254  // If we could distinguish whether the caller specified 0 explicitly or
    255  // not, we would skip computing the key location when it's specified
    256  // explicitly.  However, this caller probably won't test tricky keyboard
    257  // events, so, it must be enough even though caller cannot set location
    258  // to 0.
    259  Maybe<uint32_t> maybeNonStandardLocation;
    260  if (!event.mLocation) {
    261    maybeNonStandardLocation = mozilla::Some(event.mLocation);
    262  }
    263 
    264  // If the key is a printable key and |.code| and/or |.keyCode| value is
    265  // not specified as non-zero explicitly, let's assume that the caller
    266  // emulates US-English keyboard's behavior (because otherwise, caller
    267  // should set both values.
    268  if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
    269    if (event.mCodeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
    270      event.mCodeNameIndex =
    271          TextInputProcessor::GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(
    272              event.mKeyValue, maybeNonStandardLocation);
    273      MOZ_ASSERT(event.mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
    274    }
    275    if (!event.mKeyCode) {
    276      event.mKeyCode =
    277          TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout(
    278              event.mKeyValue, maybeNonStandardLocation);
    279      if (!event.mKeyCode) {
    280        // Prevent to recompute keyCode in TextInputProcessor.
    281        flags |= nsITextInputProcessor::KEY_KEEP_KEYCODE_ZERO;
    282      }
    283    }
    284  }
    285  // If the key is a non-printable key, we can compute |.code| value of
    286  // usual keyboard of the platform.  Note that |.keyCode| value for
    287  // non-printable key will be computed by TextInputProcessor.  So, we need
    288  // to take care only |.code| value here.
    289  else if (event.mCodeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
    290    event.mCodeNameIndex =
    291        WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(
    292            event.mKeyNameIndex, maybeNonStandardLocation);
    293  }
    294 
    295  // Synthesize keyboard events in a DOM window which is in-process top one.
    296  // For emulating user input, this is better than dispatching the events in
    297  // the caller's DOM window because this approach can test the path redirecting
    298  // the events to focused subdocument too.  However, for now, we cannot
    299  // dispatch it via another process without big changes.  Therefore, we should
    300  // use in-process top window instead.  If you need to test the path in the
    301  // parent process to, please file a feature request bug.
    302  nsCOMPtr<nsPIDOMWindowInner> windowInner =
    303      do_QueryInterface(aGlobalObject.GetAsSupports());
    304  if (!windowInner) {
    305    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
    306    return;
    307  }
    308 
    309  nsPIDOMWindowOuter* inProcessTopWindowOuter =
    310      windowInner->GetInProcessScriptableTop();
    311  if (NS_WARN_IF(!inProcessTopWindowOuter)) {
    312    aRv.Throw(NS_ERROR_FAILURE);
    313    return;
    314  }
    315 
    316  nsIDocShell* docShell = inProcessTopWindowOuter->GetDocShell();
    317  if (NS_WARN_IF(!docShell)) {
    318    aRv.Throw(NS_ERROR_FAILURE);
    319    return;
    320  }
    321 
    322  RefPtr<nsPresContext> presContext = docShell->GetPresContext();
    323  if (NS_WARN_IF(!presContext)) {
    324    aRv.Throw(NS_ERROR_FAILURE);
    325    return;
    326  }
    327 
    328  event.mWidget = presContext->GetRootWidget();
    329  if (NS_WARN_IF(!event.mWidget)) {
    330    aRv.Throw(NS_ERROR_FAILURE);
    331    return;
    332  }
    333 
    334  nsCOMPtr<nsPIDOMWindowInner> inProcessTopWindowInner =
    335      inProcessTopWindowOuter->EnsureInnerWindow();
    336  if (NS_WARN_IF(!inProcessTopWindowInner)) {
    337    aRv.Throw(NS_ERROR_FAILURE);
    338    return;
    339  }
    340 
    341  RefPtr<TextInputProcessor> textInputProcessor = new TextInputProcessor();
    342  bool beganInputTransaction = false;
    343  aRv = textInputProcessor->BeginInputTransactionForFuzzing(
    344      inProcessTopWindowInner, nullptr, &beganInputTransaction);
    345  if (NS_WARN_IF(aRv.Failed())) {
    346    return;
    347  }
    348  if (NS_WARN_IF(!beganInputTransaction)) {
    349    // This is possible if a keyboard event listener or something tries to
    350    // dispatch next keyboard events during dispatching a keyboard event via
    351    // TextInputProcessor.
    352    aRv.Throw(NS_ERROR_FAILURE);
    353    return;
    354  }
    355 
    356  // First, activate necessary modifiers.
    357  // MOZ_KnownLive(event.mWidget) is safe because `event` is an instance in
    358  // the stack, and `mWidget` is `nsCOMPtr<nsIWidget>`.
    359  Modifiers activatedModifiers = ActivateModifiers(
    360      textInputProcessor, event.mModifiers, MOZ_KnownLive(event.mWidget), aRv);
    361  if (NS_WARN_IF(aRv.Failed())) {
    362    return;
    363  }
    364 
    365  // Then, dispatch keydown and keypress.
    366  aRv = textInputProcessor->Keydown(event, flags);
    367  if (NS_WARN_IF(aRv.Failed())) {
    368    return;
    369  }
    370 
    371  // Then, dispatch keyup.
    372  aRv = textInputProcessor->Keyup(event, flags);
    373  if (NS_WARN_IF(aRv.Failed())) {
    374    return;
    375  }
    376 
    377  // Finally, inactivate some modifiers which are activated by this call.
    378  // MOZ_KnownLive(event.mWidget) is safe because `event` is an instance in
    379  // the stack, and `mWidget` is `nsCOMPtr<nsIWidget>`.
    380  InactivateModifiers(textInputProcessor, activatedModifiers,
    381                      MOZ_KnownLive(event.mWidget), aRv);
    382  if (NS_WARN_IF(aRv.Failed())) {
    383    return;
    384  }
    385 
    386  // Unfortunately, we cannot keep storing modifier state in the
    387  // TextInputProcessor since if we store it into a static variable,
    388  // we need to take care of resetting it when the caller wants.
    389  // However, that makes API more complicated.  So, until they need
    390  // to want
    391 }
    392 
    393 static void SpinEventLoopForCallback(nsITimer* aTimer, void* aClosure) {
    394  *static_cast<bool*>(aClosure) = true;
    395 }
    396 
    397 /* static */
    398 void FuzzingFunctions::SpinEventLoopFor(const GlobalObject&,
    399                                        uint32_t aMilliseconds) {
    400  bool didRun = false;
    401  nsCOMPtr<nsITimer> timer = NS_NewTimer();
    402  nsresult rv = timer->InitWithNamedFuncCallback(
    403      SpinEventLoopForCallback, &didRun, aMilliseconds, nsITimer::TYPE_ONE_SHOT,
    404      "FuzzingFunctions::SpinEventLoopFor"_ns);
    405  if (NS_FAILED(rv)) {
    406    return;
    407  }
    408 
    409  SpinEventLoopUntil("FuzzingFunctions::SpinEventLoopFor"_ns,
    410                     [&]() { return didRun; });
    411 
    412  // Ensure the timer is stopped in case we're shutting down the process and
    413  // didn't get a chance to spin the event loop long enough.
    414  timer->Cancel();
    415 }
    416 
    417 }  // namespace mozilla::dom