Every call to this function sets up an auto variable above, <code> StringSet * tags = NULL; </code>, which you update by this call to ModuleProtocol() and then drop on the floor.  The next call to this function does it all over again.  So that's not "one at the end of agent execution", that's one per call to this function.  In this function, there was no old tags array to be free()d and you end up with a newly-allocated one, which is leaked.