I'm no expert at Java, so this isn't a decisive answer, but I figure it can't hurt to post the code here anyway for others to decipher. From SpawnerAnimals.java
:
(Comments mine)
private static boolean canCreatureTypeSpawnAtLocation(EnumCreatureType enumcreaturetype, World world, int i, int j, int k)
{
if(enumcreaturetype.getCreatureMaterial() == Material.water)
// If it's a water creature...
{
return world.getBlockMaterial(i, j, k).getIsLiquid() && !world.isBlockNormalCube(i, j + 1, k);
// Make sure it spawns in water
} else
{
return world.isBlockNormalCube(i, j - 1, k) && !world.isBlockNormalCube(i, j, k) && !world.getBlockMaterial(i, j, k).getIsLiquid() && !world.isBlockNormalCube(i, j + 1, k);
// Makes sure the block it's spawning on is opaque, that it's not water, and that there are at least 2 blocks of air overhead.
}
}
It does not appear that it checks whether the mob is spawning underground or not.
Anecdotally, wandering around for a while with an Xray texture pack produced only surface mobs, so I'm not sure what other guards might be put in place to prevent underground spawns.