sysadmin ~ devops ~ full-stack js
0,0
рейтинг
16 марта в 15:34

Разработка → Простой и не очень очевидный способ повесить chrome, firefox и nodejs внутри нативной функции из песочницы

Вот весь код: var x = []; x[0x7fffffff]=1; JSON.stringify(x);


Для желающих попробовать: jsfiddle


Таким незамысловатым способом, можно намертво повесить firefox, довести до падения вкладку хрома и повесить основной поток nodejs.

Самое примечательное в этом то, что зависание происходит на уровне нативного кода функции JSON.stringify, что не позволяет прервать выполнение в том же firefox'е, как это обычно бывает при простом while(true);.

При выполнении внутри WebWorker'а в chrome, страница продолжает отвечать, но terminate не может завершить поток.

Так же по понятным причинам, такой код не обнаруживается jslint'ом.


Как это работает


var x = [];
x[0x7fffffff]=1; // предел знакового положительного 32х битного целого
JSON.stringify(x); // поскольку x массив, получаем очень много null...

Как автор докатился до жизни такой


Было 2 с лишним часа ночи, спал я уже давно и мало, а работать было надо. Нужно было реализовать коллекцию объектов в localStorage. Мозг думал уже туго, и вначале для хранения был выбран простой массив с сохранением как JSON. После осознания что с ID в данном случае работать будет удобнее, массив был заменен на объект, а для генерации случайного ID использовался следующий код Math.random() * 0x7fffffff >> 0, далее данные сериализовались и записывались в хранилище. После этого начались случайные зависания страницы, а при отладке обнаружилось что коллекция всё еще инициализировалась как массив.

Резюмируя, про себя...


На самом деле, заметка не о том что JSON.stringify плохой, а о том что надо быть внимательнее к тому, что в него отправляешь.

  1. Надо больше спать
  2. Надо чаще спать
  3. Не стоит делать числовые id, если они будут случайными(проще найти ошибку в случае чего).
  4. Нестрогая типизация иногда зло.

Заметку решил написать, когда выяснилось, что несколько знакомых программистов не раскусили сброшенный по "аське" код.
@findoff
карма
21,0
рейтинг 0,0
sysadmin ~ devops ~ full-stack js
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (30)

  • +1
    ff сказал "out of memory" и продолжил жить
    • 0
      У меня linux dev версия висела около 3х минут, пока я её не убил.
    • 0
      нечто похожее: 44-я версия на linux, немного зависла, уже собирался убить, но firefox восстановил работу.
      Это от железа может зависеть?
      • 0
        У меня 46.0a2. Возможно он убивает когда память заканчиваться начинает.
    • +1
      Подтверждаю, перед тем как выдать out of memory, FF съел дополнительный гигабайт памяти (ровно гигабайт).
  • 0
    Нестрогая типизация иногда зло.

    не совсем понимаю как тут спасет строгая типизация?
    • +3
      при отладке обнаружилось что коллекция всё еще инициализировалась как массив
      Если бы сериализовался объект, то в нём не было бы тысяч null'ов. Проверка типов помогла бы увидеть проблему на раннем этапе.
      • 0
        а, понял. при изменении надо было бы внести правки в правила сериализации?
        • +1
          Не в правила сериализации (которых в js нет), а в инициализацию объекта.
          Изначально автор работал с массивом, но потом решил превратить его в объект. А вот инициализацию исправить забыл. Поскольку ключи у него были числовые, js никак не ругался, когда в массив куда-нибудь в индекс 100500 писалось значение, хотя предыдущих индексов в нём нет. Js оптимизирует такие "дырявые" массивы и не создаёт пустые недостающие промежуточные индексы, просто устанавливает свойство length. А вот при сериализации все дыры закрываются и в итоговой строке получаются миллионы null'ов и на последнем месте единица. Вернее, должны получаться, потому что браузер в процессе виснет и до финала не доходит.
          • 0
            При использовании нечисловых индексов JS и не заругается, т.к. массив тоже объект. Просто JSON.stringify выдаст пустой массив.
        • +4
          Когда я сменил логику с books.push(book) на books[getId()] = book надо было еще и books = [] поменять на books = {}, но я этого не сделал, а JS за типами не следит, и при том что id цифровой воспринял это как номер ячейки.

          Ну и соотвественно на JSON.stringify вывел все null перед ячейкой 2e+6*random. Точнее попытался...
  • –2
    А ведь если подумать, это уязвимость. Вроде как у гуглов и т.п. за такое денежку платят =)
    • 0
      Ну это весьма косвенная уязвимость, в том плане что и Array.apply(null, Array(0x7fffffff)) тоже можно такой считать, но в нормальной ситуации внешние данные не должны влиять на индексы массива. Ну и сериализация всегда опасное дело, которое требует повышенной внимательности.
      • +1
        Сэкономлю время таким же любопытным:

        Array.apply(null, Array(0x7fffffff))
        Uncaught RangeError: Invalid array length
        • 0
          На значения по скромнее он не ругается, но да эффект конечно не такой мощный как с индексом.
      • +2
        Косвенная, возможно, только вот первое, что мне пришло в голову — https://jsfiddle.net/MrGobus/fj5xut89/, в гугле делает страницу безответной хотя отдаем должное, закрыть вкладку возможно, в файрфоксе либо смотрим ролик, либо умираем, кстати слушая звук, до момента, когда система предложит снять задачу. Несложно догадаться, что кое кому, кто любит рассказывать о том, как он чего то добился вложив 100500 денег в контору «Рога и Копыта», это может показаться удачной идеей для фокусировки внимания целевой аудитории на контенте с целью увеличения клиентской базы =) и он применит это в интернет рекламе. И вот, вместо того, чтобы смотреть пирацкий «Карточный домик» или «Наруту» многие будут слушать трели о том, как быстро разбогатеть в полной беспомощности перед такой вот «косенной» уязвимостью=)
        • 0
          Хмм, действительно интересная идея, правда сделать вкладку не закрываемой под linux-chome я так и не смог.
          Я зарепортил на хромиум. Надеюсь они поняли что я имею ввиду. (:
  • 0
    Зачем так длинно писать, если JSON.stringify(Array(0x7fffffff)) должен дать тот же эффект (или Array(2e9))?
    • +5
      JSON.stringify(Array(0x7fffffff)) писать в коде никто не будет, а у автора реальный пример из жизни. Но сократить можно, конечно.
  • +3
    Причем здесь нестрогая типизация?
    Это неверная инициализации переменной — сугубо Ваша ошибка изза усталости или неопытности, уж сами решайте.
    Например в строготипизированных языках, в которых есть указатели, можно их сложить вместо их значений, что будет в результате — сами понимаете. Но это не проблема языка, компилятор или интепритатор не должен следить за тем, что Вы пишите, он выдаст ошибку только если он не может это выполнить.

    Ударив себя молотком по пальцу — не вини молоток
    • 0
      а подвесить скорее всего и так можно

      var x = [];
      x[0x7fffffff]=0;
      console.log(x);
      • 0
        Нельзя, я выше писал, что происходит.
        • 0
          проверил, нода висит
          • 0
            Что логично, так как 'console.log(x)' это по сути 'JSON.stringify' с форматирование, обратной связью для формирования отступов и записью в stdout.

            Просто зависаний от 'console.log''а ждут многие, а от сериализации нет.
            Правда это скорее психология. Во всяком случае большинство знакомых программистов этому удивились.
            • 0
              Нет, тут дело в том, что элементарно проитерировать массив с 2e9 элементов, даже в том c++ займет время, без вывода куда либо
    • 0
      Строгая типизация уменьшает риск человеческого фактора. И если говорить про молотки, то я бы сравнил строгую типизацию с толстой перчаткой, вроде бы и палец не всегда убережёт, и мешает иногда, но на стройке без перчаток не очень...

      П.С.: Я за опциональную типизацию в JS.

      Ну а что до указателей, то я как человек учивший в 12 лет первым серьёзным языком С++, без интернета и по учебнику C#, много их наелся постигая дзен.
      • +1
        Для уменьшения фаторов риска, используют соответствующие инструменты, JSHint например и здоровый крепкий сон. И как я вижу Вы пренебрегли всем этим и пытаетесь переложить ответственность на интерпретатор и типизация тут не при чем.
        • +1
          Про сон вы правы, увы и сейчас занимаюсь тем же. Не зря же сон идет первыми двумя пунктами рекомендаций (;
          А вот jslint не обнаружил это, хотя я бы скорее удивился обратному.
    • –1
      Должен, поэтому и создаются языки типа Rust. Говорить "ахах, сам виноват" глупо, когда этой ошибки можно было избежать проверкой типов. Строгая типизация не панацея, есть и другие техники компилятора, но её было бы достаточно, чтобы вывести ошибку на этап компиляции.
  • –2
    > зависание происходит на уровне нативного кода функции JSON.stringify

    Если это действительно так, то никто из браузеров событийку пока не осилил. =(

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.