I implemented your modifications, and when I call adv_distance.backward(...) inside of the forward(...) function of VATLoss I still get None:
[...]
 def forward(self, model, x):
        with torch.autograd.set_grad_enabled(False):
            pred = F.softmax(model(x), dim=1)

        # prepare random unit tensor
        d = torch.rand(x.shape, device='cuda:0').sub(0.5)
        d_ = _l2_normalize(d)

        with _disable_tracking_bn_stats(model):
            # calc adversarial direction
            for _ in range(self.ip):
                self.dummy_d = d_
                self.dummy_d.requires_grad = True
                pred_hat = model(x + self.xi * self.dummy_d)
                logp_hat = F.log_softmax(pred_hat, dim=1)
                adv_distance = F.kl_div(logp_hat, pred, reduction='batchmean')
                adv_distance.backward(retain_graph = True)
                print(self.dummy_d.grad)
                d_ = _l2_normalize(self.dummy_d.grad)
                model.zero_grad()
[...]

The print(…) returns None. The code upstream looks like:
[...]
    model.train()
    optimizer.zero_grad()

    unlabeled_sample = nn.Parameter(unlabeled_sample, requires_grad=True)

    VATLoss_ = VATLoss()
    vat_loss_ = VATLoss_(model, unlabeled_sample) / len(unlabeled_sample)
[...]

Should I call the backward() outside of the forward() of VATLoss instead?