What arguments does a _custom_access callback exactly receive? The example given in Access checking on routes for using _custom_access is the following.

example.routing.yml

example:
  path: '/example'
  defaults:
    _controller: '\Drupal\example\Controller\ExampleController::content'
  requirements:
    _custom_access:  '\Drupal\example\Controller\ExampleController::access'


ExampleController.php

/**
 * @file
 * Contains \Drupal\example\Controller\ExamleController.
 */

namespace Drupal\example\Controller;

use Drupal\Core\Session\AccountInterface;
use Symfony\Component\HttpFoundation\Request;

    /**
     * Builds an example page.
     */
    class ExampleController {

      /**
       * Checks access for a specific request.
       *
       * @param \Drupal\Core\Session\AccountInterface $account
       *   Run access checks for this account.
       */
      public function access(AccountInterface $account) {
        // Check permissions and combine that with any custom access checking needed. Pass forward
        // parameters from the route and/or request as needed.
        return $account-&gt;hasPermission('do example things') &amp;&amp; $this-&gt;someOtherCustomCondition();
      }
    }


From this example, I get the first parameter of such callbacks is $account as I would expect, since the access is normally checked against an account. 

Looking at code used from Drupal core modules, I find the following.

toolbar.routing.yml

toolbar.subtrees:
  path: '/toolbar/subtrees/{hash}'
  defaults:
    _controller: '\Drupal\toolbar\Controller\ToolbarController::subtreesAjax'
  options:
    _theme: ajax_base_page
  requirements:
    _custom_access: '\Drupal\toolbar\Controller\ToolbarController::checkSubTreeAccess'


ToolbarController.php

public function checkSubTreeAccess($hash) {
  $expected_hash = _toolbar_get_subtrees_hash() [0];
  return AccessResult::allowedIf($this-&gt;currentUser()-&gt;hasPermission('access toolbar') &amp;&amp; Crypt::hashEquals($expected_hash, $hash))-&gt;cachePerPermissions();
}


In this case, $account is not passed as parameter, but the controller gets it from the dependency injection container, which leaves me puzzled. 

I also checked at the class implementing the access service used for _custom_access, but I just see the following code, which doesn't make things clearer.

public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) {
  $callable = $this-&gt;controllerResolver-&gt;getControllerFromDefinition($route-&gt;getRequirement('_custom_access'));
  $arguments_resolver = $this-&gt;argumentsResolverFactory-&gt;getArgumentsResolver($route_match, $account);
  $arguments = $arguments_resolver-&gt;getArguments($callable);

  return call_user_func_array($callable, $arguments);
}


(See CustomAccessCheck::access().)

If then I watch at ArgumentsResolver::getArguments() (the method called with $arguments_resolver-&gt;getArguments($callable) in the previous method), I see the following code.

public function getArguments(callable $callable) {
  $arguments = array();
  foreach ($this-&gt;getReflector($callable)-&gt;getParameters() as $parameter) {
    $arguments[] = $this-&gt;getArgument($parameter);
  }
  return $arguments;
}


$account seems ignored.

What arguments does a _custom_access callback exactly receive?
See \Drupal\Core\Access\AccessArgumentsResolverFactory::getArgumentsResolver() which is called by your snippet above to create the argument resolver, I think that covers it pretty well.

It gets all raw parameters, all possibly upcasted parameters, plus request, route match and account. The access callback receives the arguments from that list, based on the names and type hints.

So account isn't ignored.

That logic is the same as for any other access check, the whole point of _custom_access is to have an easy way to do a one-off access check without having to go through the additional steps of a service that's defined with a special tag.