# V3080. Possible null dereference.

Анализатор обнаружил фрагмент кода, который может привести к доступу по нулевой ссылке.

Рассмотрим несколько примеров, для которых анализатор выдает диагностическое сообщение V3080:

    if (obj != null || obj.Func()) { ... }
    if (obj == null && obj.Func()) { ... }
    if (list == null && list[3].Func()) { ... }

Во всех условиях допущена логическая ошибка, которая приведет к доступу по нулевой ссылке. Ошибка может быть допущена при рефакторинге кода или из-за случайно опечатки.

Корректные варианты:

    if (obj == null || obj.Func()) { .... }
    if (obj != null && obj.Func()) { .... }
    if (list != null && list[3].Func()) { .... }

Конечно, это очень простые ситуации. На практике проверка объекта на null и его использование может находиться в разных местах. Если анализатор выдал предупреждение V3080, изучите код расположенный выше и попробуйте понять, почему ссылка может быть нулевой.

Пример кода, где проверка и использование объекта находятся в разных строках

    if (player == null) {
      ....
      var identity = CreateNewIdentity(player.DisplayName);
      ....
    }

Анализатор предупредит, об опасности в строке внутри блока 'if'. Здесь или некорректно написано условие, или вместо 'player' должна использоваться другая переменная.

Программисты иногда забывают о том, что при проверке двух объектов на null один из них может оказаться нулевым, а второй нет, в результате чего будет вычислено всё условие и произойдёт доступ по нулевой ссылке. Например,

    if ((text == null && newText == null) || text.Equals(newText)) {
      ....
    }

Это условие можно переписать, например, так

    if ((text == null && newText == null) ||
        (text != null && newText != null && text.Equals(newText))) {
      ....
    }

Ещё один способ допустить ошибку заключается в использовании оператора логического AND (&) вместо условного AND (&&). Нужно помнить о том что, во-первых, при использовании логического AND всегда вычисляются обе части выражения, а во-вторых приоритет у логического AND выше чем у условного AND.

Пример:

    public static bool HasCookies {
      get {
        var context = HttpContext;
        return context != null
          && context.Request != null & context.Request.Cookies != null
          && context.Response != null && context.Response.Cookies != null;
      }
    }

В этом примере обращение к context.Request.Cookies будет выполнено даже если context.Request равен null.

Также опасной является ситуация с разыменованием параметра, для которого значением по умолчанию является 'null'. Пример:

    public NamedBucket(string name, List<object> items = null)
    {
      _name = name;

      foreach (var item in items)
      {
        ....
      }
    }

Конструктор принимает коллекцию 'items' в качестве необязательного параметра. Однако если при вызове значение для 'items' не будет передано, то при попытке обхода коллекции в 'foreach' будет выброшено исключение типа 'NullReferenceException'.

В зависимости от ситуации решение этой проблемы может отличаться. Например, можно производить обход коллекции только в случае, если она не равна 'null'.

Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки разыменования нулевого указателя/нулевой ссылки [* см. примечание касательно языков C#, Java].

Данная диагностика классифицируется как:

CWE-476

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V3080.