Как работает состояние гонки в TOCTOU?

Предполагается, что следующий код уязвим для атаки TOCTOU:

 public Period(final Date start, final Date end) {
    if (start.compare(end) > 0) {
       throw new IllegalArgumentException("");
    }

    this.start = start;
    this.end = end;      // Class period has 2 private final member 
                         // variables Date start & end.

 }

Чего я не понимаю, так это того, как будет работать это состояние гонки? Скажем, есть 2 потока T1 и T2, где T1 имеет допустимый набор аргументов и должен пройти проверку, а T2 — хакер, который хочет установить недопустимые значения в классе.

Если 2 потока соревнуются, и этот фрагмент кода является нашей критической секцией, то, скажем, T1 работает, проходит проверку и спит. Теперь, когда T2 запустится, не будет ли он снова проходить проверку (и терпеть неудачу)??


person user1071840    schedule 04.01.2014    source источник
comment
en.wikipedia.org/wiki/Time_of_check_to_time_of_use   -  person Donal Fellows    schedule 05.01.2014
comment
@JBNizet Я спрашивал об ошибке Time of Check Time of Use. У меня нет кода для потоков, так как я наткнулся на это обсуждение, читая «Эффективную Java».   -  person user1071840    schedule 05.01.2014
comment
@DonalFellows Я прочитал вики, прежде чем задать этот вопрос, я не мог понять, как там очень хорошо объясняется вариант использования. assylias очень хорошо объяснил это в своем ответе.   -  person user1071840    schedule 05.01.2014


Ответы (1)


Проблема в том, что Date является изменяемым, поэтому другой поток может изменить дату окончания: end.setTime(0); после того, как вы проверили это start.after(end) (более простой способ написать ваше условие).

Так это будет выглядеть так:

  • T1: start.after(end) => возвращает false, все выглядит хорошо
  • T2: end.setTime(0); => подлый Тема 2 меняет дату
  • T1: this.start = start; this.end = end; //boom => ваш инвариант класса больше недействителен
person assylias    schedule 04.01.2014
comment
Это еще хуже. Вам вообще не нужны нитки. Противник может переопределить compare, after и т. д. Лучше использовать new Date(date.getTime) или java.time. (Конструктора Date(Date) нет. Не используйте здесь переопределяемый метод!) - person Tom Hawtin - tackline; 04.01.2014
comment
@TomHawtin-tackline Действительно, и на самом деле модификацию, о которой я упоминаю, даже не нужно чередовать - ее можно сделать в любое время в будущем и по-прежнему влиять на этот объект. - person assylias; 04.01.2014
comment
Спасибо :) Переходя к основам, это происходит потому, что ссылки на объекты Java выделяются из кучи (общие для потоков), поэтому поможет использование примитивных переменных-членов? - person user1071840; 05.01.2014
comment
@user1071840 user1071840 Назначение локальных clone()s переданных Dates переменным поля и затем проверка порядка поможет. finals совсем не помогают. - person Donal Fellows; 05.01.2014
comment
Я это понимаю. На самом деле использование clone() также будет небезопасным, поскольку Date не является окончательным классом, а clone() позволит установить небезопасные дочерние классы Date. Новая защитная копия вроде this.start = new Date(start.getTime()) должна работать. - person user1071840; 05.01.2014