If I understand your question, you think that since x is released (by exiting the function), p should be junk. This is incorrect. first the address pointed to by x is placed on the return stack. Then x is released, because you have left the function and x is now out of scope. The return logic places the value from the return stack into p. The address obtained by the malloc was not freed and is pointed to by p.
C does not release memory until explicitly told to do so. Even if it would automatically release the memory when all references go away, the reference is still kept through the return into p.
Consider a simple arithmetic opertion
int func(a, b)
{
int c = a+b;
return(c);
}
int d = 0;
d = func(1, 2);
printf('%d\n', d);
The output is 3 even though c became junk, because the value was passed through. Similarly, the address was passed through and the contents of that memory location are still valid because you did not call free() on the allocated memory area.